summaryrefslogtreecommitdiff
path: root/mod/tex/context/third/rst
diff options
context:
space:
mode:
authorPhilipp Gesang <pgesang@ix.urz.uni-heidelberg.de>2010-12-30 16:05:45 +0100
committerPhilipp Gesang <pgesang@ix.urz.uni-heidelberg.de>2010-12-30 16:05:45 +0100
commit6dae96e6a7bb0dba0a43fc08ecf3b4614f80006c (patch)
treeac9e9dad8b326d6b39b76148d88cafba3c52fe5c /mod/tex/context/third/rst
parent1af3e6e47b9072c481aa2d40cedd3b9b99220817 (diff)
downloadcontext-rst-6dae96e6a7bb0dba0a43fc08ecf3b4614f80006c.tar.gz
module code
Diffstat (limited to 'mod/tex/context/third/rst')
-rw-r--r--mod/tex/context/third/rst/rst_context.lua1213
-rw-r--r--mod/tex/context/third/rst/rst_directives.lua256
-rw-r--r--mod/tex/context/third/rst/rst_helpers.lua632
-rw-r--r--mod/tex/context/third/rst/rst_parser.lua1460
-rw-r--r--mod/tex/context/third/rst/rst_setups.lua365
5 files changed, 3926 insertions, 0 deletions
diff --git a/mod/tex/context/third/rst/rst_context.lua b/mod/tex/context/third/rst/rst_context.lua
new file mode 100644
index 0000000..1b6e5e3
--- /dev/null
+++ b/mod/tex/context/third/rst/rst_context.lua
@@ -0,0 +1,1213 @@
+#!/usr/bin/env texlua
+--------------------------------------------------------------------------------
+-- FILE: rst_context.lua
+-- USAGE: ./rst_context.lua
+-- AUTHOR: Philipp Gesang (Phg), <megas.kapaneus@gmail.com>
+-- VERSION: 1.0
+-- CREATED: 31/08/10 19:35:15 CEST
+--------------------------------------------------------------------------------
+--
+--- TODO
+-- - Find an appropriate way to handle generic tables irrespective of the grid
+-- settings. The problem is:
+-- http://archive.contextgarden.net/message/20100912.112605.8a1aaf13.en.html
+-- Seems we'll have to choose either the grid or split tables as default. Not
+-- good.
+
+
+--local helpers
+if not context then
+ require "lpeg"
+ helpers = require "rst_helpers"
+ rst_directives = require "rst_directives"
+end
+
+if not helpers.string then print("Scheiße") os.exit() end
+
+local utf = unicode.utf8
+
+local dbg_write = helpers.dbg_writef
+
+local C, Cb, Cc, Cg, Cmt, Cp, Cs, Ct, P, R, S, V, match = lpeg.C, lpeg.Cb, lpeg.Cc, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct, lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match
+
+-- This one should ignore escaped spaces.
+do
+ local stripper = P{
+ [1] = "stripper",
+ stripper = V"space"^0 * C((V"space"^0 * (V"escaped" + V"nospace")^1)^0),
+ space = S(" \t\v\n"),
+ nospace = 1 - V"space",
+ escaped = P"\\" * V"space"
+ }
+ function string.strip(str)
+ return stripper:match(str) or ""
+ end
+end
+
+local err = function(str)
+ if str then
+ io.write("\n*[rstctx] Error: " .. str .. "\n\n")
+ end
+end
+
+rst_context = {}
+
+rst_context.collected_adornments = {}
+rst_context.last_section_level = 0
+rst_context.anonymous_targets = 0
+rst_context.anonymous_links = {}
+
+rst_context.collected_references = {}
+rst_context.context_references = {}
+rst_context.structure_references = {}
+rst_context.anonymous_set = {}
+
+rst_context.substitutions = {}
+rst_context.lastitemnumber = 0 -- enumerations in RST allow arbitrary skips
+
+rst_context.current_footnote_number = 0
+rst_context.current_symbolnote_number = 0
+
+function rst_context.addsetups(item)
+ state.addme[item] = state.addme[item] or true
+ return 0
+end
+
+function rst_context.footnote_reference (label)
+ local tf = state.footnotes
+ if label:match("^%d+$") then -- all digits
+ local c = tonumber(label)
+ --print("creating footnote nr " .. c)
+ return [[\\footnote{\\getbuffer[__footnote_number_]].. c .."]}"
+ elseif label == "#" then --autonumber
+ local rc = rst_context.current_footnote_number
+ rc = rc + 1
+ --print("creating footnote nr " .. rc)
+ rst_context.current_footnote_number = rc
+ return [[\\footnote{\\getbuffer[__footnote_number_]].. rc .."]}"
+ elseif label:match("^#.+$") then
+ local thelabel = label:match("^#(.+)$")
+ --print("creating labeled footnote " .. thelabel)
+ return [[\\footnote{\\getbuffer[__footnote_label_]].. thelabel .."]}"
+ elseif label == "*" then
+ local rc = rst_context.current_symbolnote_number
+ rc = rc + 1
+ --print("creating symbolnote nr " .. rc)
+ rst_context.current_symbolnote_number = rc
+ return [[\\symbolnote{\\getbuffer[__footnote_symbol_]].. rc .."]}"
+ else -- “citation reference” for now treating them like footnotes
+ rst_context.addsetups("citations")
+ return [[\\cite{]] .. label .. [[}]]
+ end
+end
+
+do
+ local w = S" \v\t\n" / "_"
+ local wp = Cs((w + 1)^1)
+ function rst_context.whitespace_to_underscore(str)
+ return str and wp:match(str) or ""
+ end
+end
+
+-- So we can use crefs[n][2] to refer to the place where the reference was
+-- created.
+local function get_context_reference (str)
+ local crefs = rst_context.context_references
+ local srefs = rst_context.structure_references
+ srefs[str] = true
+ refstring = "__target_" .. rst_context.whitespace_to_underscore(str)
+ crefs[#crefs + 1] = { refstring, str }
+ return refstring
+end
+
+function rst_context.emphasis (str)
+ return [[{\\em ]] .. str .. [[}]]
+end
+
+function rst_context.strong_emphasis (str)
+ return [[{\\sc ]] .. str .. [[}]]
+end
+
+function rst_context.literal (str)
+ str = str:gsub([[\]], [[\\]]) -- evade escaping of backslashes
+ return [[\\type{]] .. str .. [[}]]
+ --return [[\\starttyping ]] .. str .. [[\\stoptyping]]
+end
+
+--- ROLES for interpreted text
+
+rst_context.roles = {}
+rst_context.roles.emphasis = rst_context.emphasis
+rst_context.roles.strong_emphasis = rst_context.strong_emphasis
+rst_context.roles.literal = rst_context.literal
+rst_context.roles.bold = function(str)
+ return [[{\\bf ]] .. str .. [[}]]
+end
+
+rst_context.roles.ss = function(str)
+ return [[{\\ss ]] .. str .. [[}]]
+end
+rst_context.roles.sans_serif = rst_context.roles.ss
+
+rst_context.roles.uppercase = function(str)
+ return utf.upper(str)
+end
+
+rst_context.roles.lowercase = function(str)
+ return utf.lower(str)
+end
+
+rst_context.roles.color = function(color, str)
+ local p = helpers.patterns
+ local definition = color:match("^color_(.+)$")
+ if definition:match("^rgb_") then -- assume rgb
+ local rgb = p.rgbvalues:match(definition)
+ definition = string.format([[r=%s,g=%s,b=%s]], rgb[1], rgb[2], rgb[3])
+ end
+ return string.format([[\\colored[%s]{%s}]], definition, str)
+end
+
+--- Inofficial text roles for my private bib
+
+-- Afterthought:
+-- Different citation commands are essentially typographical instructions:
+-- they are concerned with the final representation of the data with respect to
+-- a concrete implementation. Not the thing at all that would make reST
+-- portable. But then its support for Python-style string escaping &c. ain’t at
+-- all portable either. The problem is the same with XML written to be
+-- processed with ConTeXt -- when processing the text directly in MkIV you’ll
+-- always find yourself adding setups that allow fine-grained control of the
+-- typeset output. At the same time those instructions directly contradict the
+-- main reason for XML: to provide an application-independent data markup.
+-- Typesetting XML (and now reST) with TeX, you will always end up writing TeX
+-- code disguised in XML brackets. (Btw. the docutils reST specification has
+-- the same kind of inclination to HTML -- some of its components don’t even
+-- have a meaning save in HTML peculiarities.) If you strive to avoid this
+-- *and* would like to have decent typesetting, you should use the
+-- automatically generated TeX code as a starting point for the actual
+-- typesetting job. Wish it was possible to have both -- the data in a
+-- universal form and the output in the Optimal Typesetting System -- but
+-- that’s a dream for now. If you really read these musings, then prove me
+-- wrong if you can! Or go tell those digital publishers and their willing
+-- subordinates, the authors, who think they can save a few pennys,
+-- substituting the typesetter and editor by some fancy software. Keep in mind
+-- that zapf.tex is not just random dummy text. Now, where was I?
+
+function rst_context.roles.ctsh(str) -- shorthand
+ rst_context.addsetups("citator")
+ return [[\\ctsh{]] .. str .. [[}]]
+end
+
+function rst_context.roles.ctas(str) -- short cite
+ rst_context.addsetups("citator")
+ return [[\\ctas{]] .. str .. [[}]]
+end
+
+function rst_context.roles.ctau(str) -- author only
+ rst_context.addsetups("citator")
+ return [[\\ctau{]] .. str .. [[}]]
+end
+
+function rst_context.roles.cttt(str) -- title only
+ rst_context.addsetups("citator")
+ return [[\\cttt{]] .. str .. [[}]]
+end
+
+function rst_context.roles.ctay(str) -- author year
+ rst_context.addsetups("citator")
+ return [[\\ctay{]] .. str .. [[}]]
+end
+
+function rst_context.roles.ctfu(str) -- full cite
+ rst_context.addsetups("citator")
+ return [[\\ctfu{]] .. str .. [[}]]
+end
+
+function rst_context.roles.nocite(str) -- nocite
+ rst_context.addsetups("citator")
+ return [[\\nocite[]] .. str .. [=[]]=]
+end
+
+--- End citator roles
+
+function rst_context.interpreted_text (...)
+ local tab = { ... }
+ local role, str
+ role = tab[1]:match("^:(.*):$") or tab[3]:match("^:(.*):$")
+ str = tab[2]
+
+ if not role then -- implicit role
+ role = "emphasis"
+ end
+
+ if role:match("^color_") then
+ return rst_context.roles.color(role, str)
+ end
+
+ return rst_context.roles[role](str)
+end
+
+function rst_context.link_standalone (str)
+ return "\n"
+ .. [[\\goto{\\hyphenatedurl{]] .. str .. [[}}[url(]] .. str .. [=[)]]=]
+end
+
+function rst_context.reference (str)
+ rst_context.addsetups("references")
+ str = str:match("^`?([^`]+)`?_$") -- LPEG could render this gibberish legible but not time
+ return [[\\RSTchoosegoto{__target_]] .. rst_context.whitespace_to_underscore(str) .. "}{"
+ .. str .. "}"
+end
+
+function rst_context.anon_reference (str)
+ rst_context.addsetups("references")
+ str = str:match("^`?([^`]+)`?__$")
+ rst_context.anonymous_links[#rst_context.anonymous_links+1] = str
+ link = "__target_anon_" .. #rst_context.anonymous_links
+ return string.format([[\\RSTchoosegoto{%s}{%s}]], link, str)
+end
+
+local whitespace = S" \n\t\v"
+local nowhitespace = 1 - whitespace
+local removewhitespace = Cs((nowhitespace^1 + Cs(whitespace / ""))^0)
+
+function rst_context.target (tab)
+ rst_context.addsetups("references")
+ --local tab = { ... }
+ local refs = rst_context.collected_references
+ local arefs = rst_context.anonymous_set
+ local target = tab[#tab] -- Ct + C could be clearer but who cares
+ tab[#tab] = nil
+
+ local function create_anonymous ()
+ rst_context.anonymous_targets = rst_context.anonymous_targets + 1
+ return { "anon_" .. rst_context.anonymous_targets, rst_context.anonymous_targets }
+ end
+
+ local insert = ""
+
+ if target == "" then -- links here
+ for _, id in next, tab do
+ insert = insert .. "\n\\reference[__target_" .. id .. "]{}"
+ end
+ else
+ for i=1,#tab do
+ local id = tab[i]
+ if id == "" then -- anonymous
+ local anon = create_anonymous()
+ id, arefs[anon[1]] = anon[1], anon[2]
+ else
+ id = tab[i]:gsub("\\:",":"):match("`?([^`]+)`?") -- deescaping
+ end
+ if id then
+ refs[id] = refs[id] or target
+ end
+ end
+ end
+
+ return insert
+end
+
+function rst_context.inline_internal_target (str)
+ return "\\\\reference[__target_" .. rst_context.whitespace_to_underscore(str) .."]{}"
+end
+
+function rst_context.substitution_reference (str, underscores)
+ local sub = ""
+ rst_context.addsetups("substitutions")
+ if underscores == "_" then -- normal reference
+ sub = sub .. [[\\reference[__target_]] .. rst_context.whitespace_to_underscore(string.strip(str)) .. "]{}"
+ elseif underscores == "__" then -- normal reference
+ rst_context.anonymous_targets = rst_context.anonymous_targets + 1
+ sub = sub .. [[\\reference[__target_anon_]] .. rst_context.anonymous_targets .. "]{}"
+ end
+ return sub .. [[{\\RSTsubstitution]] .. str:gsub("%s", "") .. "}"
+end
+
+function rst_context.escape (str)
+ str = str:gsub("\\(.)", "%1")
+ str = str:gsub("&", "\\letterampersand")
+ return str
+end
+
+function rst_context.joinindented (tab)
+ return table.concat (tab, "")
+end
+
+local corresponding = {
+ ['"'] = '"',
+ ["'"] = "'",
+ ["{"] = "}",
+ ["("] = ")",
+ ["["] = "]",
+ ["<"] = ">",
+}
+
+local inline_parser = P{
+ [1] = "block",
+
+ block = Cs(V"inline_as_first"^-1 * (V"except" + V"inline_element" + 1)^1),
+
+ inline_element = V"precede_inline"
+ * Cs(V"inline_do_elements")
+ * #V"succede_inline"
+ + V"footnote_reference"
+ ,
+
+ -- Ugly but needed in case the first element of a paragraph is inline
+ -- formatted.
+ inline_as_first = V"inline_do_elements" * V"succede_inline",
+
+ except = P"\\starttyping" * (1 - P"\\stoptyping")^1 * P"\\stoptyping"
+ + V"enclosed"
+ ,
+
+ inline_do_elements = V"strong_emphasis"
+ + V"substitution_reference"
+ + V"anon_reference"
+ + V"inline_literal"
+ + V"reference"
+ + V"emphasis"
+ + V"interpreted_text"
+ + V"inline_internal_target"
+ + V"link_standalone"
+ ,
+
+ precede_inline = V"spacing"
+ + V"eol"
+ + -P(1)
+ + S[['"([{<-/:]]
+ + P"‘" + P"“" + P"’" + P"«" + P"¡" + P"¿"
+ + V"inline_delimiter"
+ + P"„", -- not in standard Murkin reST
+
+ succede_inline = V"spacing"
+ + V"eol"
+ + S[['")]}>-/:.,;!?\]]
+ + P"’" + P"”" + P"»"
+ + V"inline_delimiter"
+ + -P(1)
+ + P"“" -- non-standard again but who cares
+ ,
+
+ enclosed = V"precede_inline"^-1
+ * Cg(V"quote_single" + V"quote_double" + V"leftpar", "lastgroup")
+ * V"inline_delimiter"
+ * Cmt(C(V"quote_single" + V"quote_double" + V"rightpar") * Cb("lastgroup"), function(s, i, char, oldchar)
+ return corresponding[oldchar] == char
+ end)
+ * V"succede_inline"^-1
+ * -V"underscore"
+ ,
+
+ space = P" ",
+ whitespace = (P" " + Cs(P"\t") / " " + Cs(S"\v") / " "),
+ spacing = V"whitespace"^1,
+
+ eol = P"\n",
+ --inline_delimiters = P"‐" + P"‑" + P"‒" + P"–" + V"emdash" + V"space", -- inline markup
+ inline_delimiter = P"‐" + P"‑" + P"‒" + P"–" + V"emdash" + V"space"
+ + V"bareia"
+ + V"asterisk"
+ + V"bar"
+ + V"lsquare" + V"rsquare"
+ , -- inline markup
+ asterisk = P"*",
+ quote_single = P"'",
+ quote_double = P'"',
+ double_asterisk = V"asterisk" * V"asterisk",
+ bareia = P"`",
+ backslash = P"\\",
+ bar = P"|",
+ double_bareia = V"bareia" * V"bareia",
+ escaped_bareia = (Cs(V"backslash") / "" * V"bareia") + 1,
+ colon = P":",
+ escaped_colon = (Cs(V"backslash") / "" * V"colon") + 1,
+ semicolon = P";",
+ underscore = P"_",
+ double_underscore = V"underscore" * V"underscore",
+ dot = P".",
+ interpunct = P"·",
+ comma = P",",
+ dash = P"-",
+ emdash = P"—",
+ ellipsis = P"…" + P"...",
+ exclamationmark = P"!",
+ questionmark = P"?",
+ interrobang = P"‽",
+ double_dash = V"dash" * V"dash",
+ triple_dash = V"double_dash" * V"dash",
+ hyphen = P"‐",
+ dashes = V"dash" + P"‒" + P"–" + V"emdash" + P"―",
+
+ lparenthesis = P"(",
+ rparenthesis = P")",
+ lsquare = P"[",
+ rsquare = P"]",
+ lbrace = P"{",
+ rbrace = P"}",
+ less = P"<",
+ greater = P">",
+ leftpar = V"lparenthesis" + V"lsquare" + V"lbrace" + V"less",
+ rightpar = V"rparenthesis" + V"rsquare" + V"rbrace" + V"greater",
+
+ --groupchars = S"()[]{}",
+ groupchars = V"leftpar" + V"rightpar",
+ apostrophe = P"’" + P"'",
+
+ guillemets = P"«" + P"»",
+ quotationmarks= P"‘" + P"’" + P"“" + P"”",
+ solidus= P"⁄",
+ slash = P"/",
+
+ gartenzaun = P"#",
+ digit = R"09",
+ letter = R"az" + R"AZ",
+
+ punctuation = V"apostrophe"
+ + V"colon"
+ + V"comma"
+ + V"dashes"
+ + V"dot"
+ + V"ellipsis"
+ + V"exclamationmark"
+ + V"guillemets"
+ + V"hyphen"
+ + V"interpunct"
+ + V"interrobang"
+ + V"questionmark"
+ + V"quotationmarks"
+ + V"semicolon"
+ + V"slash"
+ + V"solidus"
+ + V"underscore"
+ ,
+
+ emphasis = (V"asterisk" - V"double_asterisk")
+ * Cs((1 - V"spacing" - V"eol" - V"asterisk")
+ * ((1 - (1 * V"asterisk"))^0
+ * (1 - V"spacing" - V"eol" - V"asterisk"))^-1)
+ * V"asterisk"
+ / rst_context.emphasis,
+
+ strong_emphasis = V"double_asterisk"
+ * Cs((1 - V"spacing" - V"eol" - V"asterisk")
+ * ((1 - (1 * V"double_asterisk"))^0
+ * (1 - V"spacing" - V"eol" - V"asterisk"))^-1)
+ * V"double_asterisk"
+ / rst_context.strong_emphasis,
+
+ inline_literal = V"double_bareia"
+ * C ((V"escaped_bareia" - V"spacing" - V"eol" - V"bareia")
+ * ((V"escaped_bareia" - (1 * V"double_bareia"))^0
+ * (V"escaped_bareia" - V"spacing" - V"eol" - V"bareia"))^-1)
+ * V"double_bareia"
+ / rst_context.literal,
+
+ interpreted_text = C(V"role_marker"^-1)
+ * (V"bareia" - V"double_bareia")
+ * C ((1 - V"spacing" - V"eol" - V"bareia")
+ * ((1 - (1 * V"bareia"))^0
+ * (1 - V"spacing" - V"eol" - V"bareia"))^-1)
+ * V"bareia"
+ * C(V"role_marker"^-1)
+ / rst_context.interpreted_text,
+
+ role_marker = V"colon" * (V"backslash" * V"colon" + V"letter" + V"digit" + V"dash" + V"underscore" + V"dot")^1 * V"colon",
+
+ link_standalone = C(V"uri")
+ / rst_context.link_standalone,
+
+ anon_reference = Cs(V"anon_phrase_reference" + V"anon_normal_reference")
+ / rst_context.anon_reference,
+
+ anon_normal_reference = C((1 - V"underscore" - V"spacing" - V"eol" - V"punctuation" - V"groupchars")^1) * V"double_underscore",
+
+ anon_phrase_reference = (V"bareia" - V"double_bareia")
+ * C((1 - V"bareia")^1)
+ * V"bareia" * V"double_underscore"
+ ,
+
+ reference = Cs(V"normal_reference" + V"phrase_reference")
+ / rst_context.reference,
+
+ normal_reference = (1 - V"underscore" - V"spacing" - V"eol" - V"punctuation" - V"groupchars")^1 * V"underscore",
+
+ phrase_reference = (V"bareia" - V"double_bareia")
+ * C((1 - V"bareia")^1)
+ * V"bareia" * V"underscore"
+ ,
+
+ footnote_reference = V"lsquare"
+ * Cs(V"footnote_label" + V"citation_reference_label")
+ * V"rsquare"
+ * V"underscore"
+ / rst_context.footnote_reference
+ ,
+
+ footnote_label = V"digit"^1
+ + V"gartenzaun" * V"letter"^1
+ + V"gartenzaun"
+ + V"asterisk"
+ ,
+
+ citation_reference_label = V"letter" * (1 - V"rsquare")^1,
+
+ inline_internal_target = V"underscore"
+ * V"bareia"
+ * Cs((1 - V"bareia")^1)
+ * V"bareia"
+ / rst_context.inline_internal_target
+ ,
+
+ substitution_reference = V"bar"
+ * C((1 - V"bar")^1)
+ * V"bar"
+ * C((V"double_underscore" + V"underscore")^-1)
+ / rst_context.substitution_reference
+ ,
+
+--------------------------------------------------------------------------------
+-- Urls
+--------------------------------------------------------------------------------
+ uri = V"url_protocol" * V"url_domain" * (V"slash" * V"url_path")^0,
+
+ url_protocol = (P"http" + P"ftp" + P"shttp" + P"sftp") * P"://",
+ url_domain_char = 1 - V"dot" - V"spacing" - V"eol" - V"punctuation",
+ url_domain = V"url_domain_char"^1 * (V"dot" * V"url_domain_char"^1)^0,
+ url_path_char = R("az", "AZ", "09") + S"-_.!~*'()",
+ url_path = V"slash" * (V"url_path_char"^1 * V"slash"^-1)^1,
+}
+
+rst_context.inline_parser = inline_parser
+
+function rst_context.paragraph (data)
+ local str
+ if not data then
+ return ""
+ elseif type(data) == "table" then
+ str = #data > 1 and helpers.string.wrapat(inline_parser:match(table.concat(data, " ")), 65)
+ or inline_parser:match(data[1])
+ else
+ str = data
+ end
+ --print(str)
+ return string.format([[
+
+\\startparagraph
+%s
+\\stopparagraph
+]], str)
+end
+
+local sectionlevels = {
+ [1] = "chapter",
+ [2] = "section",
+ [3] = "subsection",
+ [4] = "subsubsection",
+ [5] = "subsubsubsection",
+}
+
+local function get_line_pattern (chr)
+ return P(chr)^1 * (-P(1))
+end
+
+function rst_context.section (...) -- TODO general cleanup; move validity
+ local tab = { ... } -- checking to parser.
+ local section, str = true, ""
+ local adornchar
+ local ulen = unicode.utf8.len
+ if #tab == 3 then -- TODO use unicode length with ConTeXt
+ adornchar = tab[1]:sub(1,1)
+ section = ulen(tab[1]) >= ulen(tab[2])
+ --section = get_line_pattern(adornchar):match(tab[1]) ~= nil and section
+ str = string.strip(tab[2])
+ else -- no overline
+ adornchar = tab[2]:sub(1,1)
+ section = ulen(tab[1]) <= ulen(tab[2])
+ --section = get_line_pattern(adornchar):match(tab[2]) ~= nil and section
+ str = tab[1]
+ end
+
+ if section then -- determine level
+ local level = rst_context.last_section_level
+ local rca = rst_context.collected_adornments
+ if rca[adornchar] then
+ level = rca[adornchar]
+ else
+ level = level + 1
+ rca[adornchar] = level
+ rst_context.last_section_level = level
+ end
+
+ ref = get_context_reference (str)
+
+ str = string.format("\n\\\\%s[%s]{%s}\n", sectionlevels[level], ref, str)
+ else
+ return [[{\\bf fix your sectioning!}\\endgraf}]]
+ end
+
+ return section and str or ""
+end
+
+-- Prime time for the fancybreak module.
+function rst_context.transition (str)
+ rst_context.addsetups("breaks")
+ --return "\\fancybreak\n"
+ return "\\fancybreak{$* * *$}\n"
+end
+
+function rst_context.bullet_marker(str)
+ return "marker"
+end
+
+-- This one should ignore escaped spaces.
+do
+ local stripper = P{
+ [1] = "stripper",
+ stripper = V"space"^0 * C((V"space"^0 * V"nospace"^1)^0),
+ space = S(" \t\v\n"),
+ escaped = P"\\" * V"space",
+ nospace = V"escaped" + (1 - V"space"),
+ }
+ function string.strip(str)
+ return stripper:match(str) or ""
+ end
+end
+
+local enumeration_types = {
+ ["*"] = "*", -- unordered bulleted
+ ["+"] = "*",
+ ["-"] = "*",
+ ["•"] = "*",
+ ["‣"] = "*",
+ ["⁃"] = "*",
+
+ ["#"] = "n", -- numbered lists and conversion
+ ["A"] = "A",
+ ["a"] = "a",
+ ["I"] = "R",
+ ["i"] = "r",
+}
+
+-- \setupitemize[left=(, right=), margin=4em, stopper=]
+
+local stripme = S"()."
+local dontstrip = 1 - stripme
+local itemstripper = stripme^0 * C(dontstrip^1) * stripme^0
+
+local function parse_itemstring(str)
+ local offset = nil
+ local setup = ",fit][itemalign=flushright,"
+ -- string.match is slightly faster than string.find
+ if str:match("^%(") then
+ setup = setup .. [[left=(,]]
+ end
+ if str:match("%)$") then
+ setup = setup .. [[right=)]]
+ end
+ if str:match("%.$") then
+ setup = setup .. [[stopper={.\\space}]]
+ end
+ local num = str:match("^%d")
+ if num then
+ -- http://thread.gmane.org/gmane.comp.tex.context/61728/focus=61729
+ setup = setup .. ",start=" .. num
+ str = "n"
+ end
+
+ str = itemstripper:match(str)
+ str = enumeration_types[str] or str
+ return {setup = setup, str = str}
+end
+
+function rst_context.startitemize(str)
+ local setup = ""
+ local result = ""
+ str = string.strip(str)
+
+ local listtype = enumeration_types[str] or parse_itemstring(str)
+
+ if type(listtype) == "table" then
+ setup = listtype.setup
+ listtype = listtype.str
+ end
+
+ result = [[
+\\startitemize[]] .. listtype .. setup .. [[]
+]]
+ return result
+end
+
+local last_item = {} -- stack
+local current_itemdepth = 0
+function rst_context.stopitemize(str)
+ last_item[current_itemdepth] = nil
+ current_itemdepth = current_itemdepth - 1
+ return str .. [[
+\\stopitemize
+]]
+end
+
+function rst_context.bullet_item (tab)
+ local li = last_item
+ -- The capture of the first item has the \startitemize as
+ -- *second* element in the array.
+ local content = #tab == 2 and tab[2] or tab[3]
+ local startstr = #tab == 3 and tab[2] or nil
+ local itemtype = tab[1]
+ local result = startstr or ""
+ if startstr then
+ current_itemdepth = current_itemdepth + 1
+ li[current_itemdepth] = itemtype
+ elseif li[current_itemdepth] then
+ if helpers.list.successor(itemtype, li[current_itemdepth]) then
+ -- just leave it alone
+ elseif helpers.list.greater(itemtype, li[current_itemdepth]) then
+ local itemnum = tonumber(string.strip(itemtype)) or helpers.list.get_decimal(itemtype)
+ result = result .. string.format([[
+\\setnumber[itemgroup:itemize]{%s}
+]], itemnum)
+ end
+ li[current_itemdepth] = itemtype
+ end
+
+ return result .. [[
+
+\\item ]] .. inline_parser:match(content) .. [[
+
+]]
+end
+
+--------------------------------------------------------------------------------
+-- Definition lists
+--------------------------------------------------------------------------------
+-- TODO define proper setups (probably bnf-like and some narrower for def-paragraphs)
+
+function rst_context.deflist (list)
+ rst_context.addsetups("deflist")
+
+ local deflist = [[
+\\startRSTdefinitionlist
+]]
+ for nd, item in ipairs(list) do
+ local term = item[1]
+ local nc = 2
+ local tmp = [[
+
+ \\RSTdeflistterm{]] .. string.strip(term) .. "}"
+ if #item > 2 then
+ while nc < #item do
+ tmp = tmp .. [[
+
+ \\RSTdeflistclassifier{]] .. string.strip(item[nc]) .. "}"
+ nc = nc + 1
+ end
+ end
+ tmp = tmp .. [[
+
+ \\RSTdeflistdefinition{%
+]]
+ for np, par in ipairs(item[#item]) do -- definitions, multi-paragraph
+ tmp = tmp .. [[
+ \\RSTdeflistparagraph{%
+]] .. inline_parser:match(par) .. "}\n"
+ end
+ tmp = tmp .. " }"
+ deflist = deflist .. tmp
+ end
+ return deflist .. [[
+
+\\stopRSTdefinitionlist
+]]
+end
+
+--------------------------------------------------------------------------------
+-- Field lists
+--------------------------------------------------------------------------------
+
+-- TODO Do something useful with field lists. For now I'm not sure what as the
+-- bibliography directives from the reST specification seem to make sense only
+-- when using docinfo and, after all, we have .bib files that are portable.
+
+function rst_context.field_list (str)
+ rst_context.addsetups("fieldlist")
+ return [[
+
+\\startRSTfieldlist]] .. str .. [[\\eTABLEbody\\stopRSTfieldlist
+]]
+end
+
+function rst_context.field_name (str)
+ return [[\\fieldname{]] .. str .. [[}]]
+end
+
+function rst_context.field_body (str)
+ return [[\\fieldbody{]] .. inline_parser:match(str) .. [[}]]
+end
+
+function rst_context.field (tab)
+ local name, body = tab[1], tab[2]
+ return string.format([[
+
+ \\RSTfieldname{%s}
+ \\RSTfieldbody{%s}
+]], name, inline_parser:match(body))
+end
+
+function rst_context.line_comment (str)
+ return "% " .. str
+end
+
+function rst_context.block_comment (str)
+ return string.format([[
+
+\iffalse
+%s\fi
+]], str)
+end
+
+function rst_context.option_list (str)
+ return [[
+\\setupTABLE[c][first] [background=color, backgroundcolor=grey, style=\tt]
+\\setupTABLE[c][each] [frame=off]
+\\setupTABLE[r][each] [frame=off]
+\\bTABLE[split=yes,option=stretch]
+\\bTABLEhead
+\\bTR
+ \\bTH Option \\eTH
+ \\bTH Description \\eTH
+\\eTR
+\\eTABLEhead
+\\bTABLEbody
+]] .. inline_parser:match(str) .. [[
+
+\\eTABLEbody
+\\eTABLE
+]]
+end
+
+function rst_context.option_item (tab)
+ return string.format([[\\bTR\\bTC %s \\eTC\\bTC %s \\eTC\\eTR
+]], tab[1], tab[2])
+end
+
+function rst_context.test(str)
+ return ":"
+end
+
+function rst_context.literal_block (str, included)
+ local indent = P" "^1
+ --local stripme = indent:match(str) or 0
+ local stripme = #str
+ for line in str:gmatch("[^\n]+") do
+ -- setting to the lowest indend of all lines
+ local idt = indent:match(line)
+ if line and idt then
+ stripme = idt < stripme and idt or stripme
+ end
+ end
+
+ local strip = P{
+ [1] = "strip",
+ strip = Cs(V"line"^1),
+ eol = P"\n",
+ restofline = (1 - V"eol")^0,
+ stop = Cs(V"eol" * P" "^0) * -P(1) / "", -- remove trailing blank lines
+ line = Cs(V"restofline" * (V"stop" + V"eol")) / function (line)
+ return #line > stripme and line:sub(stripme) or line
+ end,
+ }
+
+ str = strip:match(str)
+ str = [[
+
+\starttyping[lines=hyphenated]
+]] .. str .. [[
+
+\stoptyping
+]]
+ if included then -- escaping can ruin your day
+ str = str:gsub("\\", "\\\\")
+ end
+ return str
+end
+
+function rst_context.included_literal_block (str)
+ return rst_context.literal_block(str, true)
+end
+
+function rst_context.line_block (str)
+ rst_context.addsetups("lines")
+ return [[
+
+\\startlines
+]] .. inline_parser:match(str) .. [[\\stoplines
+]]
+end
+
+function rst_context.line_block_line(str)
+ str = str:gsub("\n", " ")
+ return str .. "\n"
+end
+
+function rst_context.line_block_empty()
+ return "\n"
+end
+
+function rst_context.block_quote (tab)
+ rst_context.addsetups("blockquote")
+ local str = [[
+\\startlinecorrection
+\\blank[small]
+\\startblockquote
+]] .. inline_parser:match(tab[1]) .. [[
+
+\\stopblockquote
+]]
+
+ return tab[2] and str .. [[
+\\blank[small]
+\\startattribution
+]] .. inline_parser:match(tab[2]) .. [[
+\\stopattribution
+\\blank[small]
+\\stoplinecorrection
+]] or str .. [[
+\\blank[small]
+\\stoplinecorrection
+]]
+end
+
+--function rst_context.table (str)
+ --return [[
+--\\startlinecorrection
+--]] .. str .. [[
+
+--\\stoplinecorrection
+--]]
+--end
+
+function rst_context.grid_table (tab)
+ local body = ""
+ local nr = 1
+ local head
+ if tab.has_head then
+ head = [[
+\\setupTABLE[c][each] [frame=off]
+\\setupTABLE[r][each] [frame=off]
+%\\startlinecorrection
+\\bTABLE[split=repeat,option=stretch]
+\\bTABLEhead
+]]
+ while nr <= tab.head_end do
+ local r = tab.rows[nr]
+ --for i,r in ipairs(tab.rows) do
+ local isempty = true
+ for n, cell in ipairs(r) do
+ if cell.variant == "normal" then
+ isempty = false
+ break
+ end
+ end
+
+ if not isempty then
+ local row = [[\\bTR]]
+ for n,c in ipairs(r) do
+ if not (c.parent or
+ c.variant == "separator") then
+ local celltext = inline_parser:match(c.stripped)
+ if c.span.x or c.span.y then
+ local span_exp = "["
+ if c.span.x then
+ span_exp = span_exp .. "nc=" .. c.span.x .. ","
+ end
+ if c.span.y then
+ span_exp = span_exp .. "nr=" .. c.span.y
+ end
+ celltext = span_exp .. "] " .. celltext
+
+ end
+
+ row = row .. "\n " .. [[\\bTH ]] .. celltext .. [[\\eTH]]
+ end
+ end
+ head = head .. row .. "\n" .. [[\\eTR]] .. "\n"
+ end
+ nr = nr + 1
+ end
+ head = head .. [[
+\\eTABLEhead
+\\bTABLEbody
+]]
+ else
+ head = [[
+\\setupTABLE[c][each] [frame=off]
+\\setupTABLE[r][each] [frame=off]
+%\\startlinecorrection
+\\bTABLE[split=repeat,option=stretch]
+\\bTABLEbody
+]]
+ end
+ while nr <= #tab.rows do
+ local r = tab.rows[nr]
+ --for i,r in ipairs(tab.rows) do
+ local isempty = true
+ for n, cell in ipairs(r) do
+ if cell.variant == "normal" then
+ isempty = false
+ break
+ end
+ end
+
+ if not isempty then
+ local row = [[\\bTR]]
+ for n,c in ipairs(r) do
+ if not (c.parent or
+ c.variant == "separator") then
+ local celltext = inline_parser:match(c.stripped)
+ if c.span.x or c.span.y then
+ local span_exp = "["
+ if c.span.x then
+ span_exp = span_exp .. "nc=" .. c.span.x .. ","
+ end
+ if c.span.y then
+ span_exp = span_exp .. "nr=" .. c.span.y
+ end
+ celltext = span_exp .. "] " .. celltext
+
+ end
+
+ row = row .. "\n " .. [[\\bTC ]] .. celltext .. [[\\eTC]]
+ end
+ end
+ body = body .. row .. "\n" .. [[\\eTR]] .. "\n"
+ end
+ nr = nr + 1
+ end
+ local tail = [[
+\\eTABLEbody
+\\eTABLE
+%\\stoplinecorrection
+]]
+ return head .. body .. tail
+end
+
+
+function rst_context.simple_table(tab)
+ local head
+ local nr = 1
+ if tab.head_end then
+ head = [[
+\\setupTABLE[c][each] [frame=off]
+\\setupTABLE[r][each] [frame=off]
+%\\startlinecorrection
+\\bTABLE[split=yes,option=stretch]
+\\bTABLEhead
+]]
+ while nr <= tab.head_end do
+ local row = tab[nr]
+ if not row.ignore then
+ dbg_write(">hr>" .. #row)
+ head = head .. [[\\bTR]]
+ for nc,cell in ipairs(row) do
+ dbg_write("%7s | ", cell.content)
+ local celltext = inline_parser:match(cell.content)
+ if cell.span then
+ head = head .. string.format([=[\\bTH[nc=%s]%s\\eTH]=], cell.span.x, celltext)
+ else
+ head = head .. [[\\bTH ]] .. celltext .. [[\\eTH]]
+ end
+ end
+ dbg_write("\n")
+ head = head .. "\\\\eTR\n"
+ end
+ nr = nr + 1
+ end
+
+ head = head .. [[
+\\eTABLEhead
+\\bTABLEbody
+]]
+ else
+ head = [[
+\\setupTABLE[c][each] [frame=off]
+\\setupTABLE[r][each] [frame=off]
+%\\startlinecorrection
+\\bTABLE[split=yes,option=stretch]
+\\bTABLEbody
+]]
+ end
+ local tail = [[
+\\eTABLEbody
+\\eTABLE
+%\\stoplinecorrection
+]]
+ local body = ""
+ while nr <= #tab do
+ local row = tab[nr]
+ if not row.ignore then
+ dbg_write(">tr>" .. #row)
+ body = body .. [[\\bTR]]
+ for nc,cell in ipairs(row) do
+ dbg_write("%7s | ", cell.content)
+ local celltext = inline_parser:match(cell.content)
+ if cell.span then
+ body = body .. string.format([=[\\bTC[nc=%s]%s\\eTC]=], cell.span.x, celltext)
+ else
+ body = body .. [[\\bTC ]] .. celltext .. [[\\eTC]]
+ end
+ end
+ dbg_write("\n")
+ body = body .. "\\\\eTR\n"
+ end
+ nr = nr + 1
+ end
+ return head .. body .. tail
+end
+
+function rst_context.footnote (label, content)
+ local tf = state.footnotes
+ rst_context.addsetups("footnotes")
+ if label:match("^%d+$") then -- all digits
+ tf.numbered[tonumber(label)] = rst_context.escape(inline_parser:match(content))
+ elseif label == "#" then --autonumber
+ repeat -- until next unrequested number
+ tf.autonumber = tf.autonumber + 1
+ until tf.numbered[tf.autonumber] == nil
+ tf.numbered[tf.autonumber] = rst_context.escape(inline_parser:match(content))
+ elseif label:match("^#.+$") then
+ local thelabel = label:match("^#(.+)$")
+ tf.autolabel[thelabel] = rst_context.escape(inline_parser:match(content))
+ elseif label == "*" then
+ rst_context.addsetups("footnote_symbol")
+ tf.symbol[#tf.symbol+1] = rst_context.escape(inline_parser:match(content))
+ else -- “citation reference” treated like ordinary footnote
+ repeat -- until next unrequested number
+ tf.autonumber = tf.autonumber + 1
+ until tf.numbered[tf.autonumber] == nil
+ tf.numbered[tf.autonumber] = rst_context.escape(inline_parser:match(content))
+ end
+ return ""
+end
+
+function rst_context.substitution_definition (subtext, directive, data)
+ data = table.concat(data, "\n")
+ local rs = rst_context.substitutions
+ rs[subtext] = { directive = directive, data = data }
+ return ""
+end
+
+-- not to be confused with the directive definition table rst_directives
+function rst_context.directive(directive, ...)
+ local rd = rst_directives
+ rst_context.addsetups("directive")
+ local data = {...}
+ local result = ""
+ if rd[directive] then
+ result = rd[directive](data)
+ end
+ return result
+end
+
+return rst_context
diff --git a/mod/tex/context/third/rst/rst_directives.lua b/mod/tex/context/third/rst/rst_directives.lua
new file mode 100644
index 0000000..0d4b4e9
--- /dev/null
+++ b/mod/tex/context/third/rst/rst_directives.lua
@@ -0,0 +1,256 @@
+#!/usr/bin/env texlua
+--------------------------------------------------------------------------------
+-- FILE: rst_directives.lua
+-- USAGE: ./rst_directives.lua
+-- AUTHOR: Philipp Gesang (Phg), <megas.kapaneus@gmail.com>
+-- VERSION: 1.0
+-- CREATED: 22/09/10 20:26:40 CEST
+--------------------------------------------------------------------------------
+--
+
+--------------------------------------------------------------------------------
+-- Directives for use with |substitutions|
+--------------------------------------------------------------------------------
+
+rst_directives = {}
+rst_directives.anonymous = 0
+rst_directives.images = {}
+rst_directives.images.done = {}
+rst_directives.images.values = {}
+
+
+rst_directives.images.keys = {
+ ["width"] = "width",
+ ["size"] = "width",
+ ["caption"] = "caption",
+ ["alt"] = "caption",
+ ["scale"] = "scale",
+}
+
+rst_directives.images.values.scale = function (orig)
+ -- http://wiki.contextgarden.net/Reference/en/useexternalfigure
+ -- scale=1000 is original size; to get 72%, use scale=720.
+ return tonumber(orig) * 1000
+end
+
+rst_directives.images.values.width = {
+ ["fit"] = "\\hsize",
+ ["hsize"] = "\\hsize",
+ ["broad"] = "\\hsize",
+ ["normal"] = "local",
+ ["normal"] = "local",
+}
+
+-- we won't allow passing arbitrary setups to context
+rst_directives.images.permitted_setups = {
+ "width", "scale"
+}
+
+local function img_setup (properties)
+ local result = ""
+ for _, prop in next, rst_directives.images.permitted_setups do
+ if properties[prop] then
+ result = result .. prop .. "=" .. properties[prop] .. ","
+ end
+ end
+ if result ~= "" then
+ result = "[" .. result .. "]"
+ end
+ return result
+end
+
+rst_directives.image = function(name, data)
+ local inline_parser = rst_context.inline_parser
+ local properties = {}
+ local anon = false
+ local rd = rst_directives
+ if not data then -- this makes the “name” argument optional
+ data = name
+ rd.anonymous = rd.anonymous + 1
+ anon = true -- indicates a nameless picture
+ name = "anonymous" .. rd.anonymous
+ end
+ properties.caption = name
+ --properties.width = "\\local"
+
+ local processed = "" -- stub; TODO do something useful with optional dimension specifications
+ if type(data) == "table" then -- should always be true
+ local p = helpers.patterns
+ for _, str in ipairs(data) do
+ local key, val
+ key, val = p.colon_keyval:match(str)
+ local rdi = rst_directives.images
+ if key and val then
+ key = rdi.keys[key] -- sanitize key expression
+ if type(rdi.values[key]) == "table" then
+ val = rdi.values[key][val]
+ elseif type(rdi.values[key]) == "function" then
+ val = rdi.values[key](val)
+ end
+ properties[key] = val
+ else
+ processed = processed .. (str and str ~= "" and string.strip(str))
+ end
+ end
+ end
+ properties.setup = img_setup(properties) or ""
+ data = processed
+ processed = nil
+ local img = ""
+ local images_done = rd.images.done
+ if not anon then
+ if not images_done[name] then
+ img = img .. string.format([[
+
+\useexternalfigure[%s][%s][]
+]], name, data)
+ images_done[name] = true
+ end
+ img = img .. string.format([[
+\def\RSTsubstitution%s{%%
+ \placefigure[here]{%s}{\externalfigure[%s]%s}
+}
+]], name, rst_context.escape(inline_parser:match(properties.caption)), name, properties.setup)
+ else -- image won't be referenced but used instantly
+ img = img .. string.format([[
+
+\placefigure[here]{%s}{\externalfigure[%s]%s}
+]], rst_context.escape(inline_parser:match(properties.caption)), data, properties.setup)
+ end
+ return img
+end
+
+rst_directives.caution = function(raw)
+ local inline_parser = rst_context.inline_parser
+ rst_context.addsetups("dbend")
+ rst_context.addsetups("caution")
+ local text
+ local first = true
+ for _, line in ipairs(raw) do
+ if not helpers.patterns.spacesonly:match(line) then
+ if first then
+ text = line
+ first = false
+ else
+ text = text .. " " .. line
+ end
+ end
+ end
+ text = rst_context.escape(helpers.string.wrapat(inline_parser:match(text)))
+ return string.format([[
+\startRSTcaution
+%s
+\stopRSTcaution
+]], text)
+end
+
+rst_directives.danger = function(raw)
+ local inline_parser = rst_context.inline_parser
+ rst_context.addsetups("dbend")
+ rst_context.addsetups("danger")
+ local text
+ local first = true
+ for _, line in ipairs(raw) do
+ if not helpers.patterns.spacesonly:match(line) then
+ if first then
+ text = line
+ first = false
+ else
+ text = text .. " " .. line
+ end
+ end
+ end
+ text = rst_context.escape(helpers.string.wrapat(inline_parser:match(text)))
+ return string.format([[
+\startRSTdanger
+%s
+\stopRSTdanger
+]], text)
+end
+
+-- http://docutils.sourceforge.net/docs/ref/rst/directives.html
+rst_directives.DANGER = function(addendum)
+ local result = ""
+ for _,str in ipairs(addendum) do
+ result = result .. (string.strip(str))
+ end
+ return string.format([[
+
+%% The Rabbit of Caerbannog
+\startlinecorrection
+\blank[force,big]
+\framed[frame=on,
+ corner=round,
+ rulethickness=5pt,
+ align=middle,
+ width=\hsize,
+ frameoffset=.5em,
+ backgroundoffset=1em,
+ background=color,
+ backgroundcolor=red,
+ foreground=color,
+ foregroundcolor=black]{%%
+ \language[en-gb]\tfb\bf
+ Follow only if ye be men of valour, for the entrance to this cave is guarded
+ by a creature so foul, so cruel that no man yet has fought with it and lived.
+ Bones of full fifty men lie strewn about its lair. So, brave knights, if you
+ do doubt your courage or your strength, come no further, for death awaits you
+ all with nasty, big, pointy teeth.%%
+ \blank[force,big]
+ %s%%
+}
+\blank[force,big]
+\stoplinecorrection
+]], result)
+end
+
+rst_directives.mp = function(name, data)
+ local mpcode = string.format([[
+\startreusableMPgraphic{%s}
+%s
+\stopreusableMPgraphic
+]], name, data)
+ mpcode = mpcode .. string.format([[
+\def\RSTsubstitution%s{%%
+ \reuseMPgraphic{%s}%%
+}
+]], name, name)
+ return mpcode
+end
+
+rst_directives.ctx = function(name, data)
+ local ctx = string.format([[
+
+\startbuffer[%s]
+%s
+\stopbuffer
+\def\RSTsubstitution%s{%%
+ \getbuffer[%s]
+}
+]], name, data, name, name)
+ return ctx
+end
+
+rst_directives.lua = function(name, data)
+ local luacode = string.format([[
+
+\startbuffer[%s]
+\startluacode
+%s
+\stopluacode
+\stopbuffer
+\def\RSTsubstitution%s{%%
+ \getbuffer[%s]%%
+}
+]], name, data, name, name)
+ return luacode
+end
+
+rst_directives.replace = function(name, data)
+ return string.format([[
+
+\def\RSTsubstitution%s{%s}
+]], name, data)
+end
+
+return rst_directives
diff --git a/mod/tex/context/third/rst/rst_helpers.lua b/mod/tex/context/third/rst/rst_helpers.lua
new file mode 100644
index 0000000..f081559
--- /dev/null
+++ b/mod/tex/context/third/rst/rst_helpers.lua
@@ -0,0 +1,632 @@
+#!/usr/bin/env texlua
+--------------------------------------------------------------------------------
+-- FILE: rst_helpers.lua
+-- USAGE: ./rst_helpers.lua
+-- DESCRIPTION: Complement to the reStructuredText parser
+-- AUTHOR: Philipp Gesang (Phg), <megas.kapaneus@gmail.com>
+-- VERSION: 1.0
+-- CREATED: 07/09/10 01:03:08 CEST
+--------------------------------------------------------------------------------
+--
+
+local utf = unicode.utf8
+local P, R, S, V, match
+ = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match
+
+local C, Carg, Cb, Cc, Cg, Cmt, Cp, Cs, Ct
+ = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct
+
+helpers = helpers or {}
+helpers.table = {}
+helpers.cell = {}
+
+function helpers.dbg_writef(...)
+ if helpers_debug == true then
+ io.write(string.format(...))
+ end
+end
+
+--helpers.dbg_write = dbg_write
+local dbg_write = helpers.dbg_writef
+
+helpers.patterns = {}
+
+do
+ local p = helpers.patterns
+ p.dash = P"-"
+ p.equals = P"="
+ p.plus = P"+"
+ p.bar = P"|"
+ p.eol = P"\n"
+ p.last = -P(1)
+ p.space = P" "
+
+ p.dash_or_equals = p.dash + p.equals
+
+ p.celldelim = p.bar + p.plus
+ p.cellcontent = (1 - p.celldelim)
+ p.cell = p.celldelim * C((1 - p.celldelim)^1) * #p.celldelim
+ p.cell_line = p.plus * p.dash^1 * #p.plus
+ p.dashesonly = p.dash^1 * p.last
+ p.spacesonly = p.space^1 * p.last
+
+ p.col_start = Cp() * p.dash_or_equals^1
+ p.col_stop = p.dash_or_equals^1 * Cp()
+ p.column_starts = p.col_start * ( p.space^1 * p.col_start)^1
+ p.column_stops = p.col_stop * ( p.space^1 * p.col_stop)^1
+
+ p.st_headsep = p.equals^1 * (p.space^1 * p.equals^1)^1
+ p.st_colspan = p.dash^1 * (p.space^1 * p.dash^1)^0 * p.space^0 * p.last
+ p.st_span_starts = Ct(Cp() * p.dash^1 * (p.space^1 * Cp() * p.dash^1)^0)
+ p.st_span_stops = Ct(p.dash^1 * Cp() * (p.space^1 * p.dash^1 * Cp())^0)
+
+
+ p.cells = P{
+ [1] = "cells",
+ cells = p.celldelim
+ * (C(V"in_cell")
+ * (V"matchwidth" * C(V"in_cell")) ^1),
+
+ in_cell = p.cellcontent^1
+ + (p.dash - p.cellcontent)^1,
+
+ matchwidth = Cmt(C(p.celldelim) * Carg(1), function(s,i,del, layout)
+ local pos = 1
+ local lw = layout.widths
+ for n=1, #lw do
+ pos = pos + lw[n] + 1
+ if (i - 1) == pos then return true end
+ end
+ return false
+ end),
+ }
+
+ p.sep_line = p.plus * (p.dash^1 * p.plus)^1 * p.last
+ p.sep_head = p.plus * (p.equals^1 * p.plus)^1 * p.last
+
+ p.sep_part = ((1 - p.cell_line)^0 * p.cell_line) - p.sep_line
+
+ p.new_row = p.sep_line + p.sep_head + p.sep_part
+
+ p.whitespace = S" \t\v\r\n"^1
+ p.strip = p.whitespace^0 * C((1 - (p.whitespace * p.last))^1) * p.whitespace^0 * p.last
+
+
+ local colon = P":"
+ local escaped_colon = P"\\:"
+ local nocolon = (escaped_colon + (1 - colon))^1
+ p.colon_right = nocolon * colon
+ p.colon_keyval = C(nocolon) * colon * p.space^1 * C((1 - (p.space^0 * P(-1)))^1)
+
+ -- color expression matching for text roles
+ local digit = R"09"
+ local dot = P"."
+ local colvalue = digit * dot * digit^1
+ + digit
+ + dot * digit^1
+ local coldelim = P"_" + P"-"
+ p.rgbvalues = P"rgb_"
+ * Ct( C(colvalue) * coldelim * C(colvalue) * coldelim * C(colvalue) )
+end
+
+function helpers.cell.create(raw, n_row, n_col, parent, variant)
+ local p = helpers.patterns
+ local cell = {}
+ cell.stripped = raw and p.strip:match(raw) or ""
+ cell.content = raw
+ cell.width = raw and utf.len(raw) or 0
+ cell.bytes = raw and #raw or 0
+ cell.variant = "normal" -- [normal|separator|y_continue|x_continue]
+ cell.pos = {}
+ cell.pos.x = n_col
+ cell.pos.y = n_row
+ cell.span = {}
+ cell.span.x = 1
+ cell.span.y = 1
+ cell.parent = parent
+ return cell
+end
+
+function helpers.cell.get_x_span(content, layout, init)
+ local acc = 0
+ local lw = layout.widths
+ for n=init, #lw do
+ acc = acc + lw[n] + 1
+ if utf.len(content) + 1 == acc then
+ return n - init
+ end
+ end
+ return false
+end
+
+
+-- Extending a cell by 1 cell horizontally.
+function helpers.cell.add_x (cell)
+ cell.span.x = cell.span.x + 1
+end
+
+
+local function set_layout (line)
+ local p = helpers.patterns
+ local layout = {}
+ local slice = Ct((p.plus * C(p.dash^1) * #p.plus)^1)
+
+ layout.widths = {}
+ layout.slices = {}
+ for n, elm in ipairs(slice:match(line)) do
+ layout.widths[n] = #elm
+ layout.slices[n] = elm
+ end
+ return layout
+end
+
+helpers_debug = true
+function helpers.table.create(raw)
+ local newtab = {}
+ newtab.rows = {}
+ newtab.layout = set_layout(raw[1])
+
+ local p = helpers.patterns
+
+ newtab.resolve_parent = function(row, col, array)
+ local array = array or newtab.rows
+ local cell = array[row][col]
+ local par_row, par_col = row, col
+ if cell.parent then
+ par_row, par_col = newtab.resolve_parent(cell.parent.y, cell.parent.x)
+ end
+ return par_row, par_col
+ end
+
+ newtab.__init = function()
+ local hc = helpers.cell
+ local rowcount = 0
+ local newtablayout = newtab.layout
+ for nr, row in ipairs(raw) do
+ newtab.rows[nr] = {}
+ local this_row = newtab.rows[nr]
+ this_row.sepline = p.sep_line:match(row)
+ this_row.sephead = p.sep_head:match(row)
+ this_row.seppart = p.sep_part:match(row)
+ if this_row.sephead then
+ newtab.has_head = true
+ newtab.head_end = nr
+ end
+
+ local splitted = { p.cells:match(row, 1, newtablayout) }
+ local pos_layout, pos_row = 1, 1
+ local make_empty = {}
+ make_empty.n, make_empty.parent = 0, nil
+
+ while pos_layout <= #newtablayout.widths do
+ local splitpos = splitted[pos_layout]
+ local layoutwidth = newtablayout.widths[pos_layout]
+ local span = 1
+ local this
+
+ if make_empty.n > 0 then
+ make_empty.n = make_empty.n - 1
+ this = hc.create("", nr, pos_layout, make_empty.parent)
+ this.parent = make_empty.parent
+ p_row, p_col = newtab.resolve_parent(this.parent.y, this.parent.x)
+ local thisparent = newtab.rows[p_row][p_col]
+ if this_row.sepline or this_row.sephead or
+ newtab.rows[p_row][p_col].variant == "separator" then
+ this.variant = "separator"
+ else
+ this.variant = "empty1"
+ end
+ else
+ local cellwidth = utf.len(splitpos)
+ if cellwidth > layoutwidth then
+ span = span + hc.get_x_span(splitpos, newtablayout, pos_layout)
+ end
+ pos_row = pos_row + span
+ this = hc.create(splitpos, nr, pos_layout, nil)
+ if p.dashesonly:match(splitpos) or
+ this_row.sepline or this_row.sephead then
+ this.variant = "separator"
+ end
+ this.span.x = span
+ make_empty.n = span - 1
+ make_empty.parent = span > 1 and { y = nr, x = pos_layout } or nil
+ end
+
+ this_row[pos_layout] = this
+ pos_layout = pos_layout + 1
+ end -- while
+ end -- for loop over rows
+
+ local oldrows = newtab.rows
+ local newrows = oldrows
+ for nc, width in ipairs(newtablayout.widths) do
+ -- this is gonna be extremely slow but at least it's readable
+ local newrow
+ local currentrow = 1
+ for nr, row in ipairs(newrows) do
+ local cell = row[nc]
+ dbg_write("nc: %s, nr:%2s | %9s | ", nc, nr,cell.variant)
+ if row.sepline or row.sephead
+ or p.dashesonly:match(cell.content)
+ or cell.variant == "separator" then -- separator; skipping and beginning new row
+ newrows[nr][nc] = cell
+ currentrow = currentrow + 1
+ newrow = true
+ dbg_write("new >%24s< ", cell.stripped)
+ if cell.parent then dbg_write("parent |") else dbg_write("no par |") end
+ else
+ dbg_write("old >%24s< ", cell.stripped)
+ if cell.parent then dbg_write("parent |") else dbg_write("no par |") end
+ if newrow then
+ newrows[nr][nc] = cell
+ currentrow = currentrow + 1
+ else -- continuing parent
+
+ local par_row, par_col
+ local parent
+ if cell.parent then
+ par_row, par_col = newtab.resolve_parent(cell.parent.y, cell.parent.x, newrows)
+ dbg_write(" use %s,%2s | ", par_col, par_row)
+ else -- Using vertical predecessor.
+ par_row, par_col = newtab.resolve_parent(nr-1,nc, newrows)
+ dbg_write(" new %s,%2s | ", par_col, par_row)
+ end
+ parent = newrows[par_row][par_col]
+
+ if newrows[nr].seppart then
+ dbg_write("span++")
+ parent.span.y = parent.span.y + 1
+ end
+
+ parent.content = parent.content .. cell.content
+ parent.stripped = parent.stripped .. " " .. cell.stripped
+ cell.variant = "empty2"
+ cell.parent = { x = par_col, y = par_row }
+ end
+ newrow = false
+ end
+ dbg_write("\n")
+ newrows[nr][nc] = cell
+ end -- for loop over rows
+ end -- for loop over columns
+ --newtab.rows = oldrows
+ newtab.rows = newrows
+ end
+
+ newtab.__init()
+
+ newtab.__draw_debug = function()
+ for nr, row in ipairs(newtab.rows) do
+ for nc, cell in ipairs(row) do
+ local field = cell.variant:sub(1,7)
+ if cell.parent then
+ field = field .. string.format(" %s,%2s",cell.parent.x, cell.parent.y)
+ end
+ dbg_write("%12s | ", field)
+ end
+ dbg_write("\n")
+ end
+ end
+
+ return newtab
+end
+
+
+
+function helpers.table.resolve_parent (row, col, array)
+ local cell = array[row][col]
+ local par_row, par_col = row, col
+ if cell.parent then
+ par_row, par_col = self.resolve_parent(cell.parent.y, cell.parent.x)
+ end
+ return par_row, par_col
+end
+
+
+-- Check the column boundaries of a simple table.
+function helpers.get_st_boundaries (str)
+ local p = helpers.patterns
+ local starts, stops, slices = {}, {}, {}
+ for n, elm in ipairs({ p.column_starts:match(str) }) do
+ slices[n] = { start = elm }
+ starts[elm] = true
+ end
+ for n, elm in ipairs({ p.column_stops :match(str) }) do
+ slices[n]["stop"] = elm
+ stops[elm] = true
+ end
+ return { starts = starts, stops = stops, slices = slices }
+end
+
+function helpers.table.simple(raw)
+ local rows = {}
+ local multispans = {}
+ local bounds = helpers.get_st_boundaries(raw[1])
+ local p = helpers.patterns
+
+ for nr, row in ipairs(raw) do
+ local newrow = {}
+ local nc = 1
+ if not p.st_headsep:match(row) and
+ not p.st_colspan:match(row) then
+ local starts, stops = {}, {}
+ local check_span = false
+ if p.st_colspan:match(raw[nr+1]) then -- expect spans over several columns
+ starts = p.st_span_starts:match(raw[nr+1])
+ stops = p.st_span_stops :match(raw[nr+1])
+ check_span = true
+ else
+ for colnr, slice in ipairs(bounds.slices) do
+ starts[colnr] = slice.start
+ stops [colnr] = slice.stop
+ end
+ end
+
+ for nc, start in ipairs(starts) do
+ -- last column can exceed layout width
+ local stop = nc ~= #starts and stops[nc] or #row
+ local cell = {
+ content = "",
+ span = { x = 1, y = 1 },
+ --ignore = false
+ }
+ cell.content = string.strip(row:sub(start, stop))
+ if check_span then
+ local start_at, stop_at
+ for colnr, slice in ipairs(bounds.slices) do
+ if slice.start == start then
+ start_at = colnr
+ end
+ if start_at and
+ not (colnr == #bounds.slices) then
+ if slice.stop == stop then
+ stop_at = colnr
+ break
+ end
+ else -- last column, width doesn't matter
+ stop_at = colnr
+ end
+ end
+ cell.span.x = 1 + stop_at - start_at
+ end
+ newrow[nc] = cell
+ end
+ elseif p.st_colspan:match(row) then
+ newrow.ignore = true
+ elseif not rows.head_end and
+ nr > 1 and #raw > nr then -- ends the header
+ rows.head_end = nr
+ newrow.head_sep = true
+ newrow.ignore = true
+ else
+ newrow.ignore = true
+ end
+ rows[nr] = newrow
+ end
+
+ for nr, row in ipairs(rows) do
+ if not row.ignore and row[1].content == "" then
+ row.ignore = true
+ for nc, cell in ipairs(row) do
+ local par_row, par_col = helpers.table.resolve_parent(nr - 1, nc, rows)
+ parent = rows[par_row][par_col]
+ parent.content = parent.content .. " " .. cell.content
+ cell.content = ""
+ end
+
+ end
+ end
+
+ return rows
+end
+
+helpers.list = {}
+
+do
+ local c = {}
+ c.roman = S"ivxlcdm"^1
+ c.Roman = S"IVXLCDM"^1
+ c.alpha = R"az" - P"i" - P"v" - P"x" - P"l"
+ c.Alpha = R"AZ" - P"I" - P"V" - P"X" - P"L"
+ c.digit = R"09"^1
+ c.auto = P"#"
+
+ local stripme = S" ()."
+ local dontstrip = 1 - stripme
+ local itemstripper = stripme^0 * C(dontstrip^1) * stripme^0
+
+ local con = function (str)
+ str = itemstripper:match(str)
+ for conv, pat in next, c do
+ if pat:match(str) then
+ return conv
+ end
+ end
+ return false
+ end
+ helpers.list.conversion = con
+
+ local rnums = {
+ i = 1,
+ v = 5,
+ x = 10,
+ l = 50,
+ c = 100,
+ d = 500,
+ m = 1000,
+ }
+
+ local function roman_to_arab (str)
+ local n = 1
+ local curr, succ
+ local max_three = { }
+ local value = 0
+ while n <= #str do
+ if curr and curr == max_three[#max_three] then
+ if #max_three >= 3 then
+ return "Not a number"
+ else
+ max_three[#max_three+1] = curr
+ end
+ else
+ max_three = { curr }
+ end
+
+ curr = rnums[str:sub(n,n)] or 1
+
+ n = n + 1
+ succ = str:sub(n,n)
+
+ if succ and succ ~= "" then
+ succ = rnums[succ]
+ if curr < succ then
+ --n = n + 1
+ --value = value + succ - curr
+ value = value - curr
+ else
+ value = value + curr
+ end
+ else
+ value = value + curr
+ end
+ end
+ return value
+ end
+ helpers.list.roman_to_arab = roman_to_arab
+
+ local suc = function (str, old)
+ str, old = itemstripper:match(str), itemstripper:match(old)
+ local n_str, n_old = tonumber(str), tonumber(old)
+ if n_str and n_old then -- arabic numeral
+ return n_str == n_old + 1
+ end
+
+ local con_str, con_old = con(str), con(old)
+ if con_str == "alpha" or
+ con_str == "Alpha" then
+ return str:byte() == old:byte() + 1
+ else -- “I'm a Roman!” - “A woman?” - “No, *Roman*! - Au!” - “So your father was a woman?”
+ if not (str:lower() == str or
+ str:upper() == str) then -- uneven cased --> fail
+ return false
+ end
+
+ local trc = state.roman_cache
+ n_str = trc[str] or nil
+ n_old = trc[old] or nil
+ if not n_str then
+ n_str = roman_to_arab(str:lower())
+ trc[str] = n_str
+ end
+ if not n_old then
+ n_old = roman_to_arab(old:lower())
+ trc[old] = n_old
+ end
+ return n_str == n_old + 1
+ end
+ end
+ helpers.list.successor = suc
+
+ local greater = function (str, old)
+ str, old = itemstripper:match(str), itemstripper:match(old)
+ local n_str, n_old = tonumber(str), tonumber(old)
+ if n_str and n_old then -- arabic numeral
+ return n_str > n_old
+ end
+
+ local con_str, con_old = con(str), con(old)
+ if con_str == "alpha" or
+ con_str == "Alpha" then
+ return str:byte() > old:byte()
+ else
+ if not (str:lower() == str or
+ str:upper() == str) then -- uneven cased --> fail
+ return false
+ end
+
+
+ local trc = state.roman_cache
+ n_str = trc[str] or nil
+ n_old = trc[old] or nil
+ if not n_str then
+ n_str = roman_to_arab(str:lower())
+ trc[str] = n_str
+ end
+ if not n_old then
+ n_old = roman_to_arab(old:lower())
+ trc[old] = n_old
+ end
+ return n_str > n_old
+ end
+ end
+ helpers.list.greater = greater
+
+ local gd = function(str)
+ str = itemstripper:match(str)
+ local value
+ local con_str = con(str)
+ if con_str == "alpha" or
+ con_str == "Alpha" then
+ return str:byte()
+ else
+ if not (str:lower() == str or
+ str:upper() == str) then
+ return false
+ end
+
+ local trc = state.roman_cache
+ n_str = trc[str] or nil
+ if not n_str then
+ n_str = roman_to_arab(str:lower())
+ trc[str] = n_str
+ end
+ return n_str
+ end
+ end
+
+ helpers.list.get_decimal = gd
+end
+
+helpers.string = {}
+
+do
+ -- This grammar inside the function is slightly faster than the same as an upvalue
+ -- with the value of “width” repeatedly given via lpeg.Carg(). This holds
+ -- for repeated calls as well.
+ local ulen = utf.len
+ function helpers.string.wrapat (str, width)
+ local width = width or 65
+ local linelength = 0
+ local wrap = P{
+ [1] = "wrapper",
+
+ wrapper = Cs(V"nowhitespace"^0 * (Cs(V"wrapme") + V"other")^1),
+ whitespace = S" \t\v" + P"\n" / function() linelength = 0 end,
+ nowhitespace = 1 - V"whitespace",
+ ignore = P[[\\type{]] * (1 - P"}")^0 * P"}",
+ -- the initial whitespace of the “other” pattern must not be
+ -- enforced (“^1”) as it will break the exceptions (“ignore”
+ -- pattern)! In general it is better to have the wrapper ignore some
+ -- valid breaks than to not have it matching some valid strings at
+ -- all.
+ other = Cmt(V"whitespace"^0 * (V"ignore" + (1 - V"whitespace")^1), function(s,i,w)
+ linelength = linelength + ulen(w)
+ return true
+ end),
+ wrapme = Cmt(V"whitespace"^1 * (1 - V"whitespace" - V"ignore")^1, function(s,i,w)
+ local lw = ulen(w)
+ if linelength + lw > width then
+ linelength = lw
+ return true
+ end
+ return false
+ end) / function (word) return "\n" .. word:match("[^%s]+") end,
+ }
+
+ local reflowed = wrap:match(str)
+ return reflowed
+ end
+end
+
+return helpers
+
diff --git a/mod/tex/context/third/rst/rst_parser.lua b/mod/tex/context/third/rst/rst_parser.lua
new file mode 100644
index 0000000..580204c
--- /dev/null
+++ b/mod/tex/context/third/rst/rst_parser.lua
@@ -0,0 +1,1460 @@
+#!/usr/bin/env texlua
+--------------------------------------------------------------------------------
+-- FILE: rst-parser.lua
+-- USAGE: ./rst-parser.lua
+-- DESCRIPTION:
+-- OPTIONS: ---
+-- REQUIREMENTS: ---
+-- AUTHOR: Philipp Gesang (Phg), <megas.kapaneus@gmail.com>
+-- VERSION: 1.0
+-- CREATED: 31/08/10 11:53:49 CEST
+--------------------------------------------------------------------------------
+--
+
+rst = {}
+helpers = {}
+optional_setups = {}
+
+if context then
+ environment.loadluafile("rst_helpers")
+ environment.loadluafile("rst_setups" )
+ environment.loadluafile("rst_context")
+ rst = rst_context
+ helpers = helpers
+ optional_setups = optional_setups
+else
+ rst = require "rst_context"
+ helpers = require "rst_helpers"
+ optional_setups = require "rst_setups"
+end
+
+
+local rst_debug = true
+
+local warn = function(str, ...)
+ if not rst_debug then return false end
+ local slen = #str + 3
+ str = "*["..str.."]"
+ for i,j in ipairs({...}) do
+ if 80 - i * 8 - slen < 0 then
+ local indent = ""
+ for i=1, slen do
+ indent = indent .. " "
+ end
+ str = str .. "\n" .. indent
+ end
+ str = str .. string.format(" |%6s", string.strip(tostring(j)))
+ end
+ io.write(str .. " |\n")
+ return 0
+end
+
+local C, Cb, Cc, Cg, Cmt, Cp, Cs, Ct
+ = lpeg.C, lpeg.Cb, lpeg.Cc, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct
+
+local P, R, S, V, match
+ = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match
+
+local utf = unicode.utf8
+
+local eol = P"\n"
+
+state = {}
+state.depth = 0
+state.bullets = {} -- mapping bullet forms to depth
+state.bullets.max = 0
+state.lastbullet = ""
+state.lastbullets = {}
+state.roman_cache = {} -- storing roman numerals that were already converted
+state.currentindent = "" -- used in definition lists and elsewhere
+state.previousindent = "" -- for literal blocks included in paragraphs to restore the paragraph indent
+state.currentwidth = 0 -- table layout
+state.currentlayout = {} -- table layout
+state.previousadorn = nil -- section underlining and overlining
+
+state.footnotes = {}
+state.footnotes.autonumber = 0
+state.footnotes.numbered = {}
+state.footnotes.labeled = {}
+state.footnotes.autolabel = {}
+state.footnotes.symbol = {}
+
+state.addme = {}
+
+do
+ local first_adornment = ""
+ local valid_adornment = P{
+ [1] = "adorncheck",
+ adorncheck = V"check_first" * V"check_other"^1 * -P(1),
+
+ -- check_first = Cg(V"adornment_char", "first"), -- This *should* work but but due to some heavenly
+ -- intervention the governing rules of the universe
+ -- have been altered so as to annoy everybody
+ -- trying to deploy it.
+
+ check_first = Cmt(V"adornment_char", function(_,_, first)
+ first_adornment = first
+ return true
+ end)
+ ,
+ check_other = Cmt(V"adornment_char", function(_,_, char)
+ local prev = first_adornment
+ return char == prev
+ end)
+ ,
+ adornment_char = S[[!"#$%&'()*+,-./:;<=>?@[]^_`{|}~]] + P[[\\]],
+ }
+ state.valid_adornment = valid_adornment
+end
+
+local enclosed_mapping = {
+ ["'"] = "'",
+ ['"'] = '"',
+ ["("] = ")",
+ ["["] = "]",
+ ["{"] = "}",
+ ["<"] = ">",
+}
+
+local utfchar = P{ -- from l-lpeg.lua, modified to use as grammar
+ [1] = "utfchar",
+ utf8byte = R("\128\191"),
+ utf8one = R("\000\127"),
+ utf8two = R("\194\223") * V"utf8byte",
+ utf8three = R("\224\239") * V"utf8byte" * V"utf8byte",
+ utf8four = R("\240\244") * V"utf8byte" * V"utf8byte" * V"utf8byte",
+ utfchar = V"utf8one" + V"utf8two" + V"utf8three" + V"utf8four",
+}
+
+
+
+local parser = P{
+ [1] = V"document",
+
+ document = V"blank_line"^0 * Cs(V"block"^1),
+
+--------------------------------------------------------------------------------
+-- Blocks
+--------------------------------------------------------------------------------
+
+ block = V"explicit_markup"
+ + Cs(V"section") / rst.escape
+ + V"target_block"
+ + V"literal_block"
+ + Cs(V"list") / rst.escape
+ + Cs(V"line_block") / rst.escape
+ + Cs(V"table_block") / rst.escape
+ + V"transition" --/ rst.escape
+ + V"comment_block"
+ + Cs(V"block_quote") / rst.escape
+ + Cs(V"paragraph") / rst.escape
+ ,
+
+--------------------------------------------------------------------------------
+-- Explicit markup block
+--------------------------------------------------------------------------------
+
+ explicit_markup_start = V"double_dot" * V"whitespace",
+
+ explicit_markup = V"footnote_block"
+ + V"directive_block"
+ + V"substitution_definition"
+ ,
+
+ explicit_markup_block = V"explicit_markup"^1
+ ,
+
+--------------------------------------------------------------------------------
+-- Directives block
+--------------------------------------------------------------------------------
+
+ directive_block = V"directive"
+ --* (V"blank_line"^-1 * V"directive")^0
+ * V"end_block"
+ ,
+
+ directive = V"explicit_markup_start"
+ * C(((V"escaped_colon" + (1 - V"colon" - V"eol")) - V"substitution_text")^1)
+ * V"double_colon"
+ * (V"directive_block_multi" + V"directive_block_single")
+ / rst.directive
+ ,
+
+ directive_block_multi = C((1 - V"eol")^0) * V"eol"
+ * V"directive_indented_lines"
+ ,
+
+ directive_block_single = C((1 - V"eol")^1) * V"eol",
+
+--------------------------------------------------------------------------------
+-- Substitution definition block
+--------------------------------------------------------------------------------
+
+ substitution_definition = V"explicit_markup_start"
+ * V"substitution_text"
+ * V"whitespace"
+ * C((1 - V"colon" - V"space" - V"eol")^1) -- directive
+ * V"double_colon"
+ * Ct(V"data_directive_block")
+ * V"end_block"^-1
+ / rst.substitution_definition
+ ,
+
+ substitution_text = V"bar"
+ * C((1 - V"bar" - V"eol")^1)
+ * V"bar"
+ ,
+
+ data_directive_block = V"data_directive_block_long"
+ + V"data_directive_block_short"
+ ,
+ data_directive_block_short = C((1 - V"eol")^0) * V"eol",
+
+ data_directive_block_long = C((1 - V"eol")^0) * V"eol"
+ * V"directive_indented_lines"
+ ,
+
+ directive_indented_lines = V"directive_indented_first"
+ * V"directive_indented_other"^0
+ ,
+
+
+ directive_indented_first = Cmt(V"space"^1, function(s,i,indent)
+ warn("sub-i", #indent, i)
+ state.currentindent = indent
+ return true
+ end)
+ * C((1 - V"eol")^1) * V"eol"
+ ,
+
+ directive_indented_other = Cmt(V"space"^1, function(s,i,indent)
+ warn("sub-m", #state.currentindent <= #indent, #indent, #state.currentindent, i)
+ return #state.currentindent <= #indent
+ end)
+ * C((1 - V"eol")^1) * V"eol"
+ ,
+
+
+--------------------------------------------------------------------------------
+-- Explicit markup footnote block
+--------------------------------------------------------------------------------
+
+ footnote_block = V"footnote"^1 * V"end_block",
+
+ footnote = V"explicit_markup_start"
+ * (V"footnote_marker" + V"citation_reference_label")
+ * C(V"footnote_content")
+ * (V"blank_line" - V"end_block")^-1
+ / rst.footnote
+ ,
+
+ footnote_marker = V"lsquare" * C(V"footnote_label") * V"rsquare" * V"whitespace"^0
+ ,
+
+ citation_reference_label = V"lsquare" * C(V"letter" * (1 - V"rsquare")^1) * V"rsquare" * V"whitespace"^0,
+
+ footnote_label = V"digit"^1
+ + (V"gartenzaun" * V"letter"^1)
+ + V"gartenzaun"
+ + V"asterisk"
+ ,
+
+ footnote_content = V"footnote_long" -- single line
+ + V"footnote_simple"
+ ,
+
+ footnote_simple = (1 - V"eol")^1 * V"eol"
+ ,
+
+ footnote_long = (1 - V"eol")^1 * V"eol"
+ * V"footnote_body"
+ ,
+
+ footnote_body = V"fn_body_first"
+ * (V"fn_body_other" + V"fn_body_other_block")^0
+ ,
+
+ fn_body_first = Cmt(V"space"^1, function(s, i, indent)
+ warn("fn-in", true, #indent)
+ state.currentindent = indent
+ return true
+ end)
+ * (1 - V"eol")^1 * V"eol"
+ ,
+
+ fn_matchindent = Cmt(V"space"^1, function(s, i, indent)
+ local tc = state.currentindent
+ warn("fn-ma", tc == indent, #tc, #indent, i)
+ return tc == indent
+ end)
+ ,
+
+ fn_body_other = V"fn_body_other_regular"
+ * (V"blank_line" * V"fn_body_other_regular")^0
+ ,
+
+ fn_body_other_regular = V"fn_matchindent"
+ * (1 - V"eol")^1 * V"eol"
+ ,
+
+ -- TODO find a way to get those to work in footnotes!
+ fn_body_other_block = V"line_block"
+ + V"table_block"
+ + V"transition"
+ + V"block_quote"
+ + V"list"
+ ,
+
+--------------------------------------------------------------------------------
+-- Table block
+--------------------------------------------------------------------------------
+
+ table_block = V"simple_table"
+ + V"grid_table"
+ ,
+
+--------------------------------------------------------------------------------
+-- Simple tables
+--------------------------------------------------------------------------------
+
+ simple_table = Ct(V"st_first_row"
+ * V"st_other_rows")
+ * V"end_block"
+ / function (tab)
+ return rst.simple_table(helpers.table.simple(tab))
+ end
+ ,
+
+ st_first_row = V"st_setindent"
+ * C(V"st_setlayout")
+ * V"space"^0
+ * V"eol"
+ ,
+
+ st_setindent = Cmt(V"space"^0, function(s, i, indent)
+ warn("sta-i", "true", #indent, "set", i)
+ state.currentindent = indent
+ return true
+ end)
+ ,
+
+ st_matchindent = Cmt(V"space"^0, function(s, i, indent)
+ warn("sta-m", state.currentindent == indent, #indent, #state.currentindent, i)
+ return state.currentindent == indent
+ end)
+ ,
+
+ st_setlayout = Cmt((V"equals"^1) * (V"spaces" * V"equals"^1)^1, function(s, i, layout)
+ local tc = state.currentlayout
+ warn("sta-l", #layout, "set", "", i)
+ tc.raw = layout
+ tc.bounds = help.get_st_boundaries(layout)
+ return true
+ end)
+ ,
+
+ st_other_rows = (V"st_content"^1 * V"st_separator")^1,
+
+ st_content = V"blank_line"^-1
+ * C(V"st_matchlayout"),
+
+ st_matchlayout = -#V"st_separator" * Cmt((1 - V"eol")^1, function (s, i, content)
+ -- Don't check for matching indent but if the rest is
+ -- fine then the line should be sane. This allows
+ -- cells starting with spaces.
+ content = content:sub(#state.currentindent)
+ local tcb = state.currentlayout.bounds
+ local n = 1
+ local spaces_only = P" "^1
+ while n < #tcb.slices do
+ local from = tcb.slices[n] .stop
+ local to = tcb.slices[n+1].start
+ local between = spaces_only:match(content, from)
+ if not between then -- Cell spanning more than one row.
+ -- pass
+ warn("sta-c", "span", from, to, i)
+ elseif not (between >= to) then
+ warn("sta-c", "false", from, to, i)
+ return false
+ end
+ n = n + 1
+ end
+ warn("sta-c", "true", #tcb.slices, "", i)
+ return true
+ end)
+ * V"eol"
+ ,
+
+ st_separator = V"st_matchindent"
+ * C(V"st_normal_sep" + V"st_colspan_sep")
+ * V"eol"
+ ,
+
+ st_normal_sep = Cmt((V"equals"^1) * (V"spaces" * V"equals"^1)^1, function(s, i, layout)
+ warn("sta-s", state.currentlayout.raw == layout, #layout, #state.currentlayout.raw, i)
+ return state.currentlayout.raw == layout
+ end)
+ ,
+
+ st_colspan_sep = Cmt(V"dash"^1 * (V"spaces" * V"dash"^1)^0, function(s, i, layout)
+ local tcb = state.currentlayout.bounds
+ local this = help.get_st_boundaries (layout)
+ local start_valid = false
+ for start, _ in next, this.starts do
+ if tcb.starts[start] then
+ start_valid = true
+ local stop_valid = false
+ for stop, _ in next, this.stops do
+ if tcb.stops[stop] then -- bingo
+ stop_valid = true
+ end
+ end
+ if not stop_valid then
+ warn("sta-x", stop_valid, #layout, #state.currentlayout.raw, i)
+ return false
+ end
+ end
+ end
+ warn("sta-x", start_valid, #layout, #state.currentlayout.raw, i)
+ return start_valid
+ end)
+ ,
+
+
+--------------------------------------------------------------------------------
+-- Grid tables
+--------------------------------------------------------------------------------
+
+ grid_table = Ct(V"gt_first_row"
+ * V"gt_other_rows")
+ * V"blank_line"^1
+ / function(tab)
+ return rst.grid_table(helpers.table.create(tab))
+ end
+ ,
+
+ gt_first_row = V"gt_setindent"
+ * C(V"gt_sethorizontal")
+ * V"eol"
+ ,
+
+ gt_setindent = Cmt(V"space"^0, function(s, i, indent)
+ warn("tab-i", true, #indent, "set", i)
+ state.currentindent = indent
+ return true
+ end)
+ ,
+
+ gt_layoutmarkers = V"table_intersection" + V"table_hline" + V"table_header_hline",
+
+ gt_sethorizontal = Cmt(V"gt_layoutmarkers"^3, function (s, i, width)
+ warn("tab-h", "width", "true", #width, "set", i)
+ state.currentwidth = #width
+ return true
+ end)
+ ,
+
+ gt_other_rows = V"gt_head"^-1
+ * V"gt_body"
+ ,
+
+ gt_matchindent = Cmt(V"space"^0, function (s, i, this)
+ local matchme = state.currentindent
+ warn("tab-m", "indent", #this == #matchme, #this, #matchme, i)
+ return #this == #matchme
+ end)
+ ,
+
+
+ gt_cell = (V"gt_content_cell" + V"gt_line_cell")
+ * (V"table_intersection" + V"table_vline")
+ ,
+
+ gt_content_cell = ((1 - V"table_vline" - V"table_intersection" - V"eol")^1),
+
+ gt_line_cell = V"table_hline"^1,
+
+ gt_contentrow = V"gt_matchindent"
+ * C((V"table_intersection" + V"table_vline")
+ * V"gt_cell"^1)
+ * V"whitespace"^-1 * V"eol"
+ ,
+
+ gt_body = ((V"gt_contentrow" - V"gt_bodysep")^1 * V"gt_bodysep")^1,
+
+ gt_bodysep = V"gt_matchindent"
+ * C(Cmt(V"table_intersection"
+ * (V"table_hline"^1 * V"table_intersection")^1, function(s, i, separator)
+ local matchme = state.currentwidth
+ warn("tab-m", "body", #separator == matchme, #separator, matchme, i)
+ return #separator == matchme
+ end))
+ * V"whitespace"^-1 * V"eol"
+ ,
+
+ gt_head = V"gt_contentrow"^1
+ * V"gt_headsep"
+ ,
+
+ gt_headsep = V"gt_matchindent"
+ * C(Cmt(V"table_intersection"
+ * (V"table_header_hline"^1 * V"table_intersection")^1, function(s, i, separator)
+ local matchme = state.currentwidth
+ warn("tab-s", "head", #separator == matchme, #separator, matchme, i)
+ return #separator == matchme
+ end))
+ * V"whitespace"^-1 * V"eol"
+ ,
+
+
+--------------------------------------------------------------------------------
+-- Block quotes
+--------------------------------------------------------------------------------
+
+ block_quote = Ct(Cs(V"block_quote_first"
+ * V"block_quote_other"^0
+ * (V"blank_line" * V"block_quote_other"^1)^0)
+ * (V"blank_line"
+ * Cs(V"block_quote_attri"))^-1)
+ * V"end_block"
+ / rst.block_quote
+ ,
+
+ block_quote_first = Cmt(V"space"^1, function (s, i, indent)
+ warn("bkq-i", #indent, "", indent, "", i)
+ state.currentindent = indent
+ return true
+ end) / ""
+ * -V"attrib_dash"
+ * (1 - V"eol")^1
+ * V"eol"
+ ,
+
+ block_quote_other = Cmt(V"space"^1, function (s, i, indent)
+ warn("bkq-m", #indent, #state.currentindent,
+ indent, state.currentindent, i)
+ return state.currentindent == indent
+ end) / ""
+ * -V"attrib_dash"
+ * (1 - V"eol")^1
+ * V"eol"
+ ,
+
+ block_quote_attri = V"block_quote_attri_first"
+ * V"block_quote_attri_other"^0,
+
+ block_quote_attri_first = Cmt(V"space"^1 * V"attrib_dash" * V"space", function (s, i, indent)
+ local t = state
+ warn("bqa-i", utf.len(indent), #t.currentindent,
+ indent, t.currentindent, i)
+ local ret = indent:match(" *") == t.currentindent
+ t.currentindent = ret and indent or t.currentindent
+ return ret
+ end) / ""
+ * (1 - V"eol")^1
+ * V"eol"
+ ,
+
+ block_quote_attri_other = Cmt(V"space"^1, function (s, i, indent)
+ warn("bqa-m", #indent, utf.len(state.currentindent),
+ indent, state.currentindent, i)
+ return utf.len(state.currentindent) == #indent
+ end) / ""
+ * (1 - V"eol")^1
+ * V"eol"
+ ,
+
+--------------------------------------------------------------------------------
+-- Line blocks
+--------------------------------------------------------------------------------
+
+ line_block = Cs(V"line_block_first"
+ * (V"line_block_other"
+ + V"line_block_empty")^1)
+ --* V"blank_line"
+ * V"end_block"
+ / rst.line_block
+ ,
+
+ line_block_marker = V"space"^0 * V"bar" * V"space",
+
+ line_block_empty_marker = V"space"^0 * V"bar" * V"space"^0 * V"eol",
+
+
+ line_block_first = Cmt(V"line_block_marker", function(s, i, marker)
+ warn("lbk-i", #marker, "", marker, "", i)
+ state.currentindent = marker
+ return true
+ end) / ""
+ * V"line_block_line"
+ ,
+
+ line_block_empty = Cmt(V"line_block_empty_marker", function(s, i, marker)
+ warn("lbk-e", #marker, #state.currentindent, marker, state.currentindent, i)
+ marker = marker:gsub("|.*", "| ")
+ return state.currentindent == marker
+ end) / ""
+ / rst.line_block_empty
+ ,
+
+ line_block_other = Cmt(V"line_block_marker", function(s, i, marker)
+ warn("lbk-m", #marker, #state.currentindent, marker, state.currentindent, i)
+ return state.currentindent == marker
+ end) / ""
+ * V"line_block_line"
+ ,
+
+ line_block_line = Cs((1 - V"eol")^1
+ * V"line_block_cont"^0
+ * V"eol")
+ / rst.line_block_line
+ ,
+
+ line_block_cont = (V"eol" - V"line_block_marker")
+ * Cmt(V"space"^1, function(s, i, spaces)
+ warn("lbk-c", #spaces, #state.currentindent, spaces, state.currentindent, i)
+ return #spaces >= #state.currentindent
+ end) / ""
+ * (1 - V"eol")^1
+ ,
+
+--------------------------------------------------------------------------------
+-- Literal blocks
+--------------------------------------------------------------------------------
+
+ literal_block = V"literal_block_marker"
+ * Cs(V"literal_block_lines")
+ * V"end_block"
+ / rst.literal_block,
+
+ literal_block_marker = V"double_colon" * V"whitespace"^0 * V"eol" * V"blank_line",
+
+ literal_block_lines = V"unquoted_literal_block_lines"
+ + V"quoted_literal_block_lines",
+
+ unquoted_literal_block_lines = V"literal_block_first"
+ * (V"blank_line"^-1 * V"literal_block_other")^0
+ ,
+
+ quoted_literal_block_lines = V"quoted_literal_block_first"
+ * (V"blank_line"^-1 * V"quoted_literal_block_other")^0
+ ,
+
+ literal_block_first = Cmt(V"space"^1, function (s, i, indent)
+ warn("lbk-f", #indent, "", "", i)
+ if not indent or
+ indent == "" then
+ return false
+ end
+ if state.currentindent and #state.currentindent < #indent then
+ state.currentindent = state.currentindent .. " "
+ return true
+ else
+ state.currentindent = " "
+ return true
+ end
+ end)
+ * V"rest_of_line"
+ * V"eol",
+
+ literal_block_other = Cmt(V"space"^1, function (s, i, indent)
+ warn("lbk-m",
+ #indent,
+ #state.currentindent,
+ #indent >= #state.currentindent,
+ i)
+ return #indent >= #state.currentindent
+ end)
+ * V"rest_of_line"
+ * V"eol",
+
+ quoted_literal_block_first = Cmt(V"adornment_char", function (s, i, indent)
+ warn("lbk-f", #indent, "", "", i)
+ if not indent or
+ indent == "" then
+ return false
+ end
+ state.currentindent = indent
+ return true
+ end)
+ * V"rest_of_line"
+ * V"eol",
+
+ quoted_literal_block_other = Cmt(V"adornment_char", function (s, i, indent)
+ warn("lbk-m",
+ #indent,
+ #state.currentindent,
+ #indent >= #state.currentindent,
+ i)
+ return #indent >= #state.currentindent
+ end)
+ * V"rest_of_line"
+ * V"eol",
+
+--------------------------------------------------------------------------------
+-- Lists
+--------------------------------------------------------------------------------
+
+ list = (V"option_list"
+ + V"bullet_list"
+ + V"definition_list"
+ + V"field_list")
+ - V"explicit_markup_start"
+ ,
+
+--------------------------------------------------------------------------------
+-- Option lists
+--------------------------------------------------------------------------------
+
+ option_list = Cs((V"option_list_item"
+ * V"blank_line"^-1)^1)
+ /rst.option_list,
+
+ option_list_item = Ct(C(V"option_group")
+ * Cs(V"option_description"))
+ / rst.option_item,
+
+ option_description = V"option_desc_next"
+ + V"option_desc_more"
+ + V"option_desc_single",
+
+ option_desc_single = V"space"^2
+ --* V"rest_of_line"
+ * (1 - V"eol")^1
+ * V"eol",
+
+ option_desc_more = V"space"^2
+ * (1 - V"eol")^1
+ * V"eol"
+ * V"indented_lines"
+ * (V"blank_line" * V"indented_lines")^0,
+
+ option_desc_next = V"eol"
+ * V"indented_lines"
+ * (V"blank_line" * V"indented_lines")^0,
+
+ option_group = V"option"
+ * (V"comma" * V"space" * V"option")^0,
+
+ option = (V"option_posixlong"
+ + V"option_posixshort"
+ + V"option_dos_vms")
+ * V"option_arg"^-1,
+
+ option_arg = (V"equals" + V"space")
+ * ((V"letter" * (V"letter" + V"digit")^1)
+ + (V"angle_left" * (1 - V"angle_right")^1 * V"angle_right")),
+
+ option_posixshort = V"dash" * (V"letter" + V"digit"),
+
+ option_posixlong = V"double_dash"
+ * V"letter"
+ * (V"letter" + V"digit" + V"dash")^1,
+
+ option_dos_vms = V"slash"
+ * V"letter"^1,
+
+--------------------------------------------------------------------------------
+-- Field lists (for bibliographies etc.)
+--------------------------------------------------------------------------------
+
+ field_list = Cs(V"field"
+ * (V"blank_line"^-1 * V"field")^0)
+ * V"end_block"
+ / rst.field_list,
+
+ field = Ct(V"field_marker"
+ * V"field_body")
+ / rst.field,
+
+ field_marker = V"colon"
+ * C(V"field_name")
+ * V"colon",
+
+ field_name = (V"escaped_colon" + (1 - V"colon"))^1,
+
+ field_body = V"field_single" + V"field_multi",
+
+ field_single = C((1 -V"eol")^1) * V"eol",
+
+ field_multi = C((1 - V"eol")^0 * V"eol"
+ * V"indented_lines"^-1),
+
+--------------------------------------------------------------------------------
+-- Definition lists
+--------------------------------------------------------------------------------
+
+ definition_list = Ct((V"definition_item" - V"comment")
+ * (V"blank_line" * V"definition_item")^0)
+ * V"end_block"
+ / rst.deflist
+ ,
+
+ definition_item = Ct(C(V"definition_term")
+ * V"definition_classifiers"
+ * V"eol"
+ * Ct(V"definition_def"))
+ ,
+
+ definition_term = #(1 - V"space" - V"field_marker")
+ * (1 - V"eol" - V"definition_classifier_separator")^1
+ ,
+
+ definition_classifier_separator = V"space" * V"colon" * V"space",
+
+ definition_classifiers = V"definition_classifier"^0,
+
+ definition_classifier = V"definition_classifier_separator"
+ * C((1 - V"eol" - V"definition_classifier_separator")^1)
+ ,
+
+ definition_def = C(V"definition_firstpar") * C(V"definition_par")^0
+ ,
+
+ definition_indent = Cmt(V"space"^1, function(s, i, indent)
+ warn("def-i", #indent, #state.currentindent, indent == state.currentindent, i)
+ state.currentindent = indent
+ return true
+ end),
+
+ definition_firstpar = V"definition_parinit"
+ * (V"definition_parline" - V"blank_line")^0
+ ,
+
+ definition_par = V"blank_line"
+ * (V"definition_parline" - V"blank_line")^1
+ ,
+
+ definition_parinit = V"definition_indent"
+ * (1 - V"eol")^1
+ * V"eol"
+ ,
+
+ definition_parline = V"definition_match"
+ * (1 - V"eol")^1
+ * V"eol"
+ ,
+
+ definition_match = Cmt(V"space"^1, function (s, i, this)
+ warn("def-m", #this, #state.currentindent, this == state.currentindent, i)
+ return this == state.currentindent
+ end),
+
+--------------------------------------------------------------------------------
+-- Bullet lists and enumerations
+--------------------------------------------------------------------------------
+
+ -- the next rule handles enumerations as well
+ bullet_list = V"bullet_init"
+ * (V"blank_line"^-1 * (V"bullet_list" + V"bullet_continue"))^1
+ * V"bullet_stop"
+ * Cmt(Cc(nil), function (s, i)
+ local t = state
+ warn("close", t.depth)
+ t.bullets[t.depth] = nil -- “pop”
+ t.depth = t.depth - 1
+ t.lastbullet = t.lastbullets[t.depth]
+ return true
+ end),
+
+ bullet_stop = V"end_block" / rst.stopitemize,
+
+ bullet_init = Ct(C(V"bullet_first") * V"bullet_itemrest")
+ / rst.bullet_item
+ ,
+
+ bullet_first = #Cmt(V"bullet_indent", function (s, i, bullet)
+ local t = state
+ local oldbullet = t.bullets[t.depth]
+ local n_spaces = match(P" "^0, bullet)
+ warn("first",
+ t.depth,
+ (t.depth == 0 and n_spaces >= 1) or
+ (t.depth > 0 and n_spaces > 1),
+ bullet,
+ oldbullet,
+ helpers.list.conversion(bullet))
+
+ if t.depth == 0 and n_spaces >= 1 then -- first level
+ t.depth = 1 -- “push”
+ t.bullets[1] = bullet
+ t.lastbullet = bullet
+ t.bullets.max = t.bullets.max < t.depth and t.depth or t.bullets.max
+ return true
+ elseif t.depth > 0 and n_spaces > 1 then -- sublist (of sublist)^0
+ if n_spaces >= utf.len(oldbullet) then
+ t.lastbullets[t.depth] = t.lastbullet
+ t.depth = t.depth + 1
+ t.bullets[t.depth] = bullet
+ t.lastbullet = bullet
+ t.bullets.max = t.bullets.max < t.depth and t.depth or t.bullets.max
+ return true
+ end
+ end
+ return false
+ end)
+ * V"bullet_indent"
+ / rst.startitemize
+ ,
+
+ bullet_indent = V"space"^0 * V"bullet_expr" * V"space"^1,
+
+ bullet_cont = Cmt(V"bullet_indent", function (s, i, bullet)
+ local t = state
+ local conversion = helpers.list.conversion
+ warn("conti",
+ t.depth,
+ bullet == t.bullets[t.depth],
+ bullet,
+ t.bullets[t.depth],
+ t.lastbullets[t.depth],
+ conversion(t.lastbullet),
+ conversion(bullet)
+ )
+
+ if utf.len(t.bullets[t.depth]) ~= utf.len(bullet) then
+ return false
+ elseif not conversion(bullet) and t.bullets[t.depth] == bullet then
+ return true
+ elseif conversion(t.lastbullet) == conversion(bullet) then -- same type
+ local autoconv = conversion(bullet) == "auto"
+ local greater = helpers.list.greater (bullet, t.lastbullet)
+ t.lastbullet = bullet
+ return autoconv or successor or greater
+ end
+ end),
+
+ bullet_continue = Ct(C(V"bullet_cont") * V"bullet_itemrest")
+ /rst.bullet_item
+ ,
+
+ bullet_itemrest = C(V"bullet_rest" -- first line
+ * ((V"bullet_match" * V"bullet_rest")^0 -- any successive lines
+ * (V"blank_line"
+ * (V"bullet_match" * (V"bullet_rest" - V"bullet_indent"))^1)^0))
+ ,
+ -- ^^^^^^^^^^^^^
+ -- otherwise matches bullet_first
+
+ bullet_rest = (1 - V"eol")^1 * V"eol", -- rest of one line
+
+ bullet_next = V"space"^1
+ ,
+
+ bullet_match = Cmt(V"bullet_next", function (s, i, this)
+ local t = state
+ warn("match",
+ t.depth,
+ string.len(this) == utf.len(t.bullets[t.depth]),
+ utf.len(t.bullets[t.depth]), string.len(this) )
+ return string.len(this) == utf.len(t.bullets[t.depth])
+ end)
+ ,
+
+ bullet_expr = V"bullet_char"
+ + (P"(" * V"number_char" * P")")
+ + (V"number_char" * P")")
+ + (V"number_char" * V"dot") * #V"space"
+ + (V"number_char" * #V"space")
+ ,
+
+ number_char = V"roman_numeral"
+ + V"Roman_numeral"
+ + P"#"
+ + V"digit"^1
+ + R"AZ"
+ + R"az"
+ ,
+
+--------------------------------------------------------------------------------
+-- Transitions
+--------------------------------------------------------------------------------
+
+ transition_line = C(V"adornment_char"^4),
+
+ transition = V"transition_line" * V"eol"
+ * V"end_block"
+ / rst.transition
+ ,
+
+--------------------------------------------------------------------------------
+-- Sectioning
+--------------------------------------------------------------------------------
+
+ section_adorn = V"adornment_char"^1,
+
+ section = ((V"section_text" * V"section_once")
+ + (V"section_before" * V"section_text" * V"section_after"))
+ / rst.section
+ * (V"end_block" + V"blank_line")
+ ,
+
+ -- The whitespace handling after the overline is necessary because headings
+ -- without overline aren't allowed to be indented.
+ section_before = C(Cmt(V"section_adorn", function(s,i, adorn)
+ state.previousadorn = adorn
+ warn ("sec-f", state.valid_adornment:match(adorn), adorn:sub(1,2) .. "...", "", i)
+ if state.valid_adornment:match(adorn) then
+ return true
+ end
+ return false
+ end))
+ * V"whitespace"^0
+ * V"eol"
+ * V"whitespace"^0
+ ,
+
+ section_text = C((1 - V"space" - V"eol") * (1 - V"eol")^1) * V"eol",
+
+ section_after = C(Cmt(V"section_adorn", function(s,i, adorn)
+ local tests = false
+ tests = state.valid_adornment:match(adorn) and true
+ if state.previousadorn then
+ tests = tests and adorn == state.previousadorn
+ end
+ warn ("sec-o", tests, adorn:sub(1,2) .. "…", "", i)
+ state.previousadorn = nil
+ return tests
+ end))
+ * V"whitespace"^0
+ ,
+
+ section_once = C(Cmt(V"section_adorn", function(s,i, adorn)
+ local tests = false
+ tests = state.valid_adornment:match(adorn) and true
+ warn ("sec-o", tests, adorn:sub(1,2) .. "…", "", i)
+ state.previousadorn = nil
+ return tests
+ end))
+ * V"whitespace"^0
+ ,
+
+--------------------------------------------------------------------------------
+-- Target Blocks
+--------------------------------------------------------------------------------
+
+ tname_normal = C((V"escaped_colon" + 1 - V"colon")^1)
+ * V"colon",
+
+ tname_bareia = C(V"bareia"
+ * (1 - V"eol" - V"bareia")^1
+ * V"bareia")
+ * V"colon",
+
+ target_name = V"double_dot"
+ * V"space"
+ * V"underscore"
+ * (V"tname_bareia" + V"tname_normal"),
+
+ target_firstindent = V"eol" * Cg(V"space"^1, "indent"),
+
+ target_nextindent = V"eol" * C(V"space"^1),
+
+ target_indentmatch = Cmt(V"target_nextindent" -- I ♡ LPEG!
+ * Cb("indent"), function (s, i, a, b)
+ return a == b
+ end),
+
+ target_link = ( V"space"^0 * V"target_firstindent"
+ * Ct(C(1 - V"whitespace" - V"eol")^1
+ * (V"target_indentmatch"
+ * C(1 - V"whitespace" - V"eol")^1)^0)
+ * V"eol" * #(1 - V"whitespace" - "eol")) / rst.joinindented
+ + C((1 - V"eol")^1) * V"eol" * #(V"double_dot" + V"double_underscore" + V"eol")
+ + (1 - V"end_block")^0 * Cc(""),
+
+ target = Ct((V"target_name" * (V"space"^0 * V"eol" * V"target_name")^0)
+ * V"space"^0
+ * V"target_link")
+ / rst.target,
+
+ anonymous_prefix = (V"double_dot" * V"space" * V"double_underscore" * V"colon")
+ + (V"double_underscore")
+ ,
+
+ anonymous_target = V"anonymous_prefix"
+ * V"space"^0
+ * Ct(Cc"" * V"target_link")
+ / rst.target
+ ,
+
+ target_block = (V"anonymous_target" + V"target")^1
+ * V"end_block",
+
+--------------------------------------------------------------------------------
+-- Paragraphs * Inline Markup
+--------------------------------------------------------------------------------
+
+ paragraph = Ct(V"par_first"
+ * V"par_other"^0) / rst.paragraph
+ * V"end_block"
+ * V"reset_depth"
+ ,
+
+ par_first = V"par_setindent" * C((1 - V"literal_block_shorthand" - V"eol")^1) * V"eol",
+
+ par_other = V"par_matchindent"
+ * C((1 - V"literal_block_shorthand" - V"eol")^1)
+ * (V"included_literal_block" + V"eol")
+ ,
+
+ par_setindent = Cmt(V"space"^0, function (s, i, indent)
+ warn("par-i", #indent, "", "", i)
+ state.previousindent = state.currentindent
+ state.currentindent = indent
+ return true
+ end),
+
+ par_matchindent = Cmt(V"space"^0, function (s, i, indent)
+ warn("par-m", state.currentindent == indent, #indent, #state.currentindent, i)
+ return state.currentindent == indent
+ end),
+
+ link_standalone = C(V"uri")
+ / rst.link_standalone,
+
+ reference = Cs(V"_reference")
+ / rst.reference,
+
+ _reference = (1 - V"underscore" - V"spacing" - V"eol" - V"punctuation" - V"groupchars")^1 * V"underscore",
+
+ included_literal_block = V"literal_block_shorthand"
+ * V"literal_block_markerless"
+ * Cmt(Cp(), function (s, i, _)
+ warn("par-s", "", #state.previousindent, #state.currentindent, i)
+ state.currentindent = state.previousindent
+ return true
+ end)
+ ,
+
+ literal_block_shorthand = Cs(((V"colon" * V"space" * V"double_colon")
+ + V"double_colon")
+ * V"whitespace"^0
+ * V"eol"
+ * V"blank_line")
+ -- The \unskip is necessary because the lines of a
+ -- paragraph get concatenated from a table with a
+ -- space as separator. And the literal block is
+ -- treated as one such line, hence it would be
+ -- preceded by a space. As the ":" character
+ -- always follows a non-space this should be a
+ -- safe, albeit unpleasant, hack. If you don't
+ -- agree then file a bug report and I'll look into
+ -- it.
+ / "\\\\unskip:"
+ ,
+
+ literal_block_markerless = Cs(V"literal_block_lines"
+ * (V"blank_line"^1 * V"literal_block_lines")^0)
+ * V"blank_line"
+ / rst.included_literal_block
+ ,
+
+ -- This is needed because lpeg.Cmt() patterns are evaluated even
+ -- if they are part of a larger pattern that doesn’t match. The
+ -- result is that they confuse the nesting.
+ -- Resetting the current nesting depth at every end of block
+ -- should be safe because this pattern always matches last.
+ reset_depth = Cmt(Cc("nothing") / "", function (s,i, something)
+ state.depth = 0
+ return true
+ end)
+ ,
+
+--------------------------------------------------------------------------------
+-- Comments
+--------------------------------------------------------------------------------
+
+ comment_block = V"comment"^1
+ * V"end_block"
+ ,
+
+ comment = V"double_dot" / ""
+ * (V"block_comment" + V"line_comment")
+ ,
+
+ block_comment = V"eol"
+ * Cs(V"indented_lines")
+ / rst.block_comment,
+
+ line_comment = V"whitespace"^1
+ * Cs((1 - V"eol")^0 * V"eol")
+ / rst.line_comment
+ ,
+
+--------------------------------------------------------------------------------
+-- Generic indented block
+--------------------------------------------------------------------------------
+
+ indented_lines = V"indented_first"
+ * (V"indented_other"^0
+ * (V"blank_line" * V"indented_other"^1)^0)
+ ,
+
+ indented_first = Cmt(V"space"^1, function (s, i, indent)
+ warn("idt-f", indent, i)
+ state.currentindent = indent
+ return true
+ end)
+ * (1 - V"eol")^1
+ * V"eol",
+
+ indented_other = Cmt(V"space"^1, function (s, i, indent)
+ warn("idt-m", #indent, #state.currentindent, #indent == #state.currentindent, i)
+ return indent == state.currentindent
+ end)
+ * (1 - V"eol")^1
+ * V"eol",
+
+--------------------------------------------------------------------------------
+-- Urls
+--------------------------------------------------------------------------------
+ uri = V"url_protocol" * V"url_domain" * (V"slash" * V"url_path")^0,
+
+ url_protocol = (P"http" + P"ftp" + P"shttp" + P"sftp") * P"://",
+ url_domain_char = 1 - V"dot" - V"spacing" - V"eol" - V"punctuation",
+ url_domain = V"url_domain_char"^1 * (V"dot" * V"url_domain_char"^1)^0,
+ url_path_char = R("az", "AZ", "09") + S"-_.!~*'()",
+ url_path = V"slash" * (V"url_path_char"^1 * V"slash"^-1)^1,
+
+--------------------------------------------------------------------------------
+-- Terminal Symbols and Low-Level Elements
+--------------------------------------------------------------------------------
+
+ word = (1 - V"punctuation" - V"end_block" - V"spacing" - V"eol")^1, -- TODO : no punctuation (later)
+
+ asterisk = P"*",
+ double_asterisk = V"asterisk" * V"asterisk",
+
+ bareia = P"`",
+ double_bareia = V"bareia" * V"bareia",
+ escaped_bareia = (Cs(V"backslash") / "" * V"bareia") + 1,
+
+ slash = P"/",
+ doubleslash = V"slash" * V"slash",
+
+ backslash = P"\\",
+ bar = P"|",
+
+ groupchars = S"()[]{}",
+
+ --- Punctuation
+ -- Some of the following are used for markup as well as for punctuation.
+
+ comma = P",",
+ colon = P":",
+ double_colon = V"colon" * V"colon",
+ escaped_colon = V"backslash" * V"colon",
+ dot = P".",
+ period = V"dot",
+ double_dot = V"dot" * V"dot",
+ interpunct = P"·",
+ underscore = P"_",
+ double_underscore = V"underscore" * V"underscore",
+ dash = P"-",
+ double_dash = V"dash" * V"dash",
+ triple_dash = V"double_dash" * V"dash",
+ emdash = P"—",
+ attrib_dash = V"triple_dash" + V"double_dash" + V"emdash", -- begins quote attribution blocks
+ dashes = V"dash" + P"‒" + P"–" + V"emdash" + P"―",
+ hyphen = P"‐",
+ semicolon = P";",
+ questionmark = P"?",
+ exclamationmark = P"!",
+ inverted_exclamationmark = P"¡",
+ inverted_questionmark = P"¿",
+ interrobang = P"‽",
+
+ apostrophe = P"’" + P"'",
+ --brackets = P"[ ], (",, { }, ⟨ ⟩ )
+ lsquare = P"[",
+ rsquare = P"]",
+ ellipsis = P"…" + P"...",
+ guillemets = P"«" + P"»",
+ quotationmarks= P"‘" + P"’" + P"“" + P"”",
+ solidus= P"⁄",
+
+
+ punctuation = V"apostrophe"
+ + V"colon"
+ + V"comma"
+ + V"dashes"
+ + V"dot"
+ + V"ellipsis"
+ + V"exclamationmark"
+ + V"guillemets"
+ + V"hyphen"
+ + V"interpunct"
+ + V"interrobang"
+ + V"questionmark"
+ + V"quotationmarks"
+ + V"semicolon"
+ + V"slash"
+ + V"solidus"
+ + V"underscore"
+ ,
+
+ -- These are treated separately as the might begin a paragraph (sigh!).
+ inverted_punctuation = V"inverted_exclamationmark"
+ + V"inverted_questionmark",
+
+ -- End punctuation
+
+ letter = R"az" + R"AZ",
+
+ equals = P"=",
+
+ space = P" ",
+ spaces = V"space"^1,
+ whitespace = (P" " + Cs(P"\t") / " " + Cs(S"\v") / " "),
+ spacing = V"whitespace"^1,
+ blank_line = V"space"^0 * V"eol",
+
+ rest_of_line = (1 - V"eol")^1,
+
+ eol = P"\n",
+ eof = V"eol"^0 * -P(1),
+
+ end_block = V"blank_line"^1
+ + V"eof"
+ + (V"whitespace"^0 * V"eol"
+ * (V"whitespace"^0 * V"eol")^0 * V"eof")
+ ,
+
+ -- diverse markup character sets
+ delimiters = P"‐" + P"‑" + P"‒" + P"–" + V"emdash" + V"space", -- inline markup
+ adornment_char = S[[!"#$%&'()*+,-./:;<=>?@[]^_`{|}~]] + P[[\\]], -- headings
+ bullet_char = S"*+-" + P"•" + P"‣" + P"⁃", -- bullet lists
+ argument_char = V"double_dash" * V"dash" * V"slash", -- option lists
+
+ digit = R"09",
+ roman_numeral = S"ivxlcdm"^1,
+ Roman_numeral = S"IVXLCDM"^1,
+
+ inline_delimiter = P"**" + P"``" + S"*`",
+ angle_left = P"<",
+ angle_right = P">",
+ enclosed_open = S[['"([{<]],
+ enclosed_close = S[['")]}>]],
+
+ gartenzaun = P"#",
+
+ table_intersection = P"+",
+ table_hline = V"dash",
+ table_vline = V"bar",
+ table_header_hline = P"=",
+}
+
+local function load_file (name)
+ f = assert(io.open(name, "r"), "Not a file!")
+ if not f then return 1 end
+ local tmp = f:read("*all")
+ f:close()
+ return tmp
+end
+
+local function save_file (name, data)
+ f = assert(io.open(name, "w"), "Could not open file "..name.." for writing! Check its permissions")
+ if not f then return 1 end
+ f:write(data)
+ f:close()
+ return 0
+end
+
+local function get_setups ()
+ local optional_setups = optional_setups -- might expect lots of calls
+ local setups = [[
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%
+%{ Setups }%
+%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% General %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\setupcolors[state=start]
+\setupinteraction[state=start,focus=standard,color=darkgreen,contrastcolor=darkgreen]
+\setupbodyfontenvironment [default] [em=italic]
+\sethyphenatedurlnormal{:=?&}
+\sethyphenatedurlbefore{?&}
+\sethyphenatedurlafter {:=/-}
+
+\doifundefined{startparagraph}{% -->mkii
+ \enableregime[utf]
+ \let\startparagraph\relax
+ \let\stopparagraph\endgraf
+}
+
+]]
+
+ for item, _ in next, state.addme do
+ local f = optional_setups[item]
+ setups = f and setups .. f() or setups
+ end
+ return setups .. [[
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%
+%{ Main }%
+%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\starttext
+]]
+end
+
+local function main()
+ local testdata = load_file(arg[1])
+ if testdata == 1 then return 1 end
+
+ local processeddata = parser:match(testdata)
+ local setups = get_setups()
+
+ processeddata = setups .. processeddata .. [[
+
+\stoptext
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%
+%{ End of Document }%
+%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% vim:ft=context:tw=65:shiftwidth=2:tabstop=2:set expandtab
+]]
+
+ if processeddata then
+ save_file(arg[2], processeddata)
+ else
+ return 1
+ end
+ return 0
+end
+
+do
+ local Cs, P = lpeg.Cs, lpeg.P
+ local percent = P"\%"
+ local eol = P"\n"
+ local comment = percent * (1 - eol)^0 * eol / "\n"
+ strip_comments = Cs((comment + 1)^0)
+end
+
+function do_rst_file(fname)
+ local rst_parser = parser
+ local raw_data = load_file(fname)
+ local processed = rst_parser:match(raw_data)
+ local setups = get_setups()
+ local tmp_file = tex.jobname .. "–rst_temporary.tex.tmp"
+
+ if processed then
+ processed = strip_comments:match(setups..processed.."\n\\stoptext\n")
+ save_file (tmp_file,processed)
+ context.input("./"..tmp_file)
+ end
+end
+
+
+if not context then
+ return main()
+end
diff --git a/mod/tex/context/third/rst/rst_setups.lua b/mod/tex/context/third/rst/rst_setups.lua
new file mode 100644
index 0000000..74a9650
--- /dev/null
+++ b/mod/tex/context/third/rst/rst_setups.lua
@@ -0,0 +1,365 @@
+#!/usr/bin/env texlua
+--------------------------------------------------------------------------------
+-- FILE: rst_setups.lua
+-- USAGE: ./rst_setups.lua
+-- DESCRIPTION: Setups for rstcontext
+-- OPTIONS: ---
+-- REQUIREMENTS: ---
+-- AUTHOR: Philipp Gesang (Phg), <megas.kapaneus@gmail.com>
+-- VERSION: 1.0
+-- CREATED: 22/09/10 20:21:01 CEST
+--------------------------------------------------------------------------------
+--
+
+if context then
+ environment.loadluafile("rst_directives")
+else
+ rst_directives = require "rst_directives"
+end
+
+optional_setups = {}
+
+function optional_setups.footnote_symbol ()
+ local setup = [[
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Footnotes with symbol conversion %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\definenote[symbolnote][footnote]
+\setupnote [symbolnote][way=bypage,numberconversion=set 2]
+]]
+ return setup
+end
+
+function optional_setups.footnotes ()
+ local tf = state.footnotes
+ local fn = [[
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Footnotes %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+]]
+ local buffer = [[
+
+%% %s
+\startbuffer[%s]
+%s\stopbuffer
+]]
+
+ for nf, note in next, tf.numbered do
+ fn = fn .. string.format(buffer, "Autonumbered footnote", "__footnote_number_"..nf, note)
+ end
+ for nf, note in next, tf.autolabel do
+ fn = fn .. string.format(buffer, "Labeled footnote", "__footnote_label_"..nf, note)
+ end
+ for nf, note in next, tf.symbol do
+ fn = fn .. string.format(buffer, "Symbol footnote", "__footnote_symbol_"..nf, note)
+ end
+ return fn
+end
+
+function optional_setups.references ()
+ local refs = rst_context.collected_references
+ local crefs = rst_context.context_references
+ local arefs = rst_context.anonymous_set
+
+ local function urlescape (str)
+ return str:gsub("#", "\\#")
+ end
+
+ local function resolve_indirect (r)
+ if r and r:match(".*_$") then -- pointing elsewhere
+ local look_me_up = r:match("^`?([^`]*)`?_$")
+ local result = resolve_indirect (refs[look_me_up])
+ if result then
+ return result
+ else
+ if rst_context.structure_references[look_me_up] then
+ -- Internal link, no useURL etc.
+ return false
+ end
+ end
+ end
+ return r
+ end
+
+ local refsection = [[
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% References %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+]]
+ local references = {}
+ local ref_keys = {}
+ for ref, target in next, refs do
+ ref_keys[#ref_keys+1] = [[__target_]] .. rst_context.whitespace_to_underscore(ref)
+ target = resolve_indirect(target)
+ if target ~= false then
+ ref_text = ref
+ if arefs[ref_text] then
+ ref_text = rst_context.anonymous_links[tonumber(arefs[ref_text])]
+ end
+ references[#references+1] = string.format([[
+\useURL[__target_%s] [%s] [] [%s] ]], rst_context.whitespace_to_underscore(ref), urlescape(target), ref_text)
+ end
+ end
+ refsection = refsection .. table.concat(references, "\n")
+ -- this is needed in order to select the right reference command later
+ refsection = refsection .. "\n\n" .. [[\def \RSTexternalreferences{]] .. table.concat(ref_keys, ",") .. [[}
+
+% #1 target name, #2 link text
+\def\RSTchoosegoto#1#2{%
+ \rawdoifinsetelse{#1}{\RSTexternalreferences}%
+ {\from[#1]}%
+ {\goto{#2}[#1]}%
+}
+]]
+
+ return refsection
+end
+
+function optional_setups.substitutions ()
+ local directives = rst_directives
+ local substitutions = [[
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Substitutions %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+]]
+ local rs = rst_context.substitutions
+ for name, content in next, rs do
+ local directive, data = content.directive, content.data
+ name, data = name:gsub("%s", ""), string.strip(data)
+ if directives[directive] then
+ substitutions = substitutions .. directives[directive](name, data)
+ else
+ err(directive .. " does not exist.")
+ end
+ end
+ return substitutions
+end
+
+function optional_setups.directive ()
+ --local dirstr = [[
+
+--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+--% Directives %
+--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+--]]
+ --return dirstr
+ return ""
+end
+
+function optional_setups.blockquote ()
+ return [[
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Blockquotes %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\setupdelimitedtext [blockquote][style={\tfx}] % awful placeholder
+\definedelimitedtext[attribution][blockquote]
+\setupdelimitedtext [attribution][style={\tfx\it}]
+]]
+end
+
+function optional_setups.deflist ()
+ return [[
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Definitionlist %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\def\startRSTdefinitionlist{
+ \bgroup
+ \def \RSTdeflistterm##1{{\bf ##1}}
+ \def\RSTdeflistclassifier##1{\hbox to 1em{\it ##1}}
+ \def\RSTdeflistdefinition##1{%
+ \startnarrower[left]
+ ##1%
+ \stopnarrower}
+ \def\RSTdeflistparagraph ##1{%
+ \startparagraph{%
+ \noindentation ##1
+ \stopparagraph}
+ }
+}
+
+\let\stopRSTdefinitionlist\egroup
+]]
+end
+
+function optional_setups.lines ()
+ return [[
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Lines environment (line blocks) %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\setuplines[%
+ space=on,%
+ before={\startlinecorrection\blank[small]},%
+ after={\blank[small]\stoplinecorrection},%
+]
+]]
+end
+
+function optional_setups.breaks ()
+ return [[
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Fancy transitions %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\usemodule[fancybreak]
+\setupfancybreak[symbol=star]
+]]
+end
+
+function optional_setups.fieldlist ()
+ return [[
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Fieldlists %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\def\startRSTfieldlist{%
+ \bgroup%
+ \unexpanded\def\RSTfieldname##1{\bTR\bTC ##1\eTC}
+ \unexpanded\def\RSTfieldbody##1{\bTC ##1\eTC\eTR}
+%
+ \setupTABLE[c][first] [background=color, backgroundcolor=grey, style=\bf]
+ \setupTABLE[c][2] [align=right]
+ \setupTABLE[c][each] [frame=off]
+ \setupTABLE[r][each] [frame=off]
+ \bTABLE[split=yes,option=stretch]
+ \bTABLEhead
+ \bTR
+ \bTH Field \eTH
+ \bTH Body \eTH
+ \eTR
+ \eTABLEhead
+ \bTABLEbody
+}
+
+\def\stopRSTfieldlist{%
+ %\eTABLEbody % doesn't work, temporarily moved to rst_context.field_list()
+ \eTABLE
+ \egroup%
+}
+]]
+end
+
+function optional_setups.dbend ()
+ -- There's just no reason for not providing this.
+ optional_setups.dbend_done = true
+ return [[
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Dangerous bend %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\loadmapfile [manfnt.map]
+\definefontsynonym [bends] [manfnt]
+
+\def\GetSym#1{\getglyph{bends}{\char#1}}
+
+\startsymbolset [Dangerous Bends]
+ \definesymbol [dbend] [\GetSym{127}]
+ \definesymbol [lhdbend] [\GetSym{126}]
+ \definesymbol [lhdbend] [\GetSym{0}]
+\stopsymbolset
+
+\setupsymbolset [Dangerous Bends]
+
+]]
+end
+
+function optional_setups.caution ()
+ local result = ""
+ --if not optional_setups.dbend_done then
+ --result = result .. optional_setups.dbend()
+ --end
+ return result .. [[
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Caution directive %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\usemodule[lettrine]
+
+\setbox0=\hbox{\symbol[dbend]}
+\newskip\RSTbendskip
+\RSTbendskip=\wd0
+\advance\RSTbendskip by 1em % These two lines should add
+\advance\RSTbendskip by 1pt % 13.4pt in mkiv and 13.14983pt in mkii
+ % to make the indent equal to the indent
+ % of the “danger” directive.
+ % (2*(width)dbend + (kern)1pt + 1em
+
+\def\startRSTcaution{%
+\startparagraph
+\dontleavehmode\lettrine[Lines=2,Raise=.6,Findent=\RSTbendskip,Nindent=0pt]{\symbol[dbend]}{}%
+}
+
+\let\stopRSTcaution\stopparagraph
+
+]]
+
+end
+
+function optional_setups.danger ()
+ local result = ""
+ --if not optional_setups.dbend_done then
+ --result = result .. optional_setups.dbend()
+ --end
+ return result .. [[
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Danger directive %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\usemodule[lettrine]
+
+\def\startRSTdanger{%
+\startparagraph
+\lettrine[Lines=2,Raise=.6,Findent=1em,Nindent=0pt]{\symbol[dbend]\kern 1pt\symbol[dbend]}{}%
+}
+
+\let\stopRSTdanger\stopparagraph
+
+]]
+
+end
+
+function optional_setups.citations ()
+ local cit = [[
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Citations %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\setupbibtex[database=\jobname]
+]]
+
+
+ return cit
+end
+
+function optional_setups.citator ()
+ local cit = [[
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Citator Options %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\usemodule[citator]
+\loadbibdb{\jobname.bib}
+\setupcitator[sortmode=authoryear]
+\setupcite[mainmode=authoryear]
+
+\startbuffer[bibliography]
+\chapter{References}
+\setupbodyfont[small]
+\bibbykey{shorthand}{all}{author}
+\stopbuffer
+
+\prependtoks \getbuffer[bibliography] \to \everystoptext
+]]
+
+ return cit
+end
+
+return optional_setups
+