From 6dae96e6a7bb0dba0a43fc08ecf3b4614f80006c Mon Sep 17 00:00:00 2001
From: Philipp Gesang <pgesang@ix.urz.uni-heidelberg.de>
Date: Thu, 30 Dec 2010 16:05:45 +0100
Subject: module code

---
 mod/tex/context/third/rst/rst_context.lua    | 1213 +++++++++++++++++++++
 mod/tex/context/third/rst/rst_directives.lua |  256 +++++
 mod/tex/context/third/rst/rst_helpers.lua    |  632 +++++++++++
 mod/tex/context/third/rst/rst_parser.lua     | 1460 ++++++++++++++++++++++++++
 mod/tex/context/third/rst/rst_setups.lua     |  365 +++++++
 5 files changed, 3926 insertions(+)
 create mode 100644 mod/tex/context/third/rst/rst_context.lua
 create mode 100644 mod/tex/context/third/rst/rst_directives.lua
 create mode 100644 mod/tex/context/third/rst/rst_helpers.lua
 create mode 100644 mod/tex/context/third/rst/rst_parser.lua
 create mode 100644 mod/tex/context/third/rst/rst_setups.lua

(limited to 'mod/tex/context/third/rst')

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
+
-- 
cgit v1.2.3