local html = require'garkup.html' local T = html.T local extensions = {} local function prose(S, text) -- this may be a hack local replacements = {} local SO, SI = "\x0e", "\x0f" -- if debugview then SO,SI = "\x1b[7m", "\x1b[0m" end local function emplace(s) table.insert(replacements, s) return SO .. #replacements .. SI end -- no way to match 'after ws or at start of string' -- so, just add ws to start and end of string -- this may also be a hack text = ' ' .. text .. ' ' local function simple_sub(delim, tagname) return { "(%s)"..delim..'(.-)'..delim..'(%W)', -- wo wimple function(a,x,b) return a..emplace(T[tagname](x))..b end } end local function strip(s) local q = s:match("^%s*(.-)%s*$") return q or s end local function split(s) local r = {} for u in s:gmatch("%w+") do table.insert(r,u) end return r end local function extension(str) local verb, rest = assert(str:match("^{%s*([^%s;]+)(.*)}$")) local args, body = rest:match("(.-);(.*)") if not args then args = rest; body = '' end verb = strip(verb) args = split(args) body = strip(body) -- return verb, args, body return emplace(extensions[verb](S, body, table.unpack(args))) end local subs = { { "%b{}", extension }, simple_sub('%*', 'strong'), simple_sub('_', 'em'), simple_sub('`', 'code'), } for i, row in ipairs(subs) do local pattern,replacement = row[1], row[2] text = text:gsub(pattern,replacement) end text = text:gsub("^%s*",""):gsub("%s*$","") -- print(text) local insects = {""} local n = 1 while n <= #text do local _, nn, id = text:find('^'..SO:gsub("%[","%%[").."(%d+)"..SI:gsub("%[","%%["), n) if nn then table.insert(insects, replacements[tonumber(id)]) table.insert(insects, "") n = nn + 1 else local c = text:sub(n,n) -- print("'"..c.."'") insects[#insects] = insects[#insects] .. c n = n + 1 end end -- for i,n in ipairs(insects) do print(i,type(n),n) end return insects end extensions.fn = function(S,text) S.footnotes = S.footnotes or {} local n = #S.footnotes + 1 local link_id = "fnref_"..n local note_id = "fn_"..n S.footnotes[n] = true -- for nested footnotes S.footnotes[n] = T.li({id=note_id}, { prose(S,text), html.safe" ", T.a({href="#"..link_id, role="doc-backlink"}, html.safe"↩︎")}) return T.sup({id=link_id}, T.a({href="#"..note_id, role='doc-noteref'}, '('..n..')')) end extensions.fns = function(S) if not S.footnotes then return "" end return {T.hr"", T.ol({class="footnotes"},S.footnotes)} end extensions.meta = function(S,text) local fn = assert(load(string.format("return {%s}", text),"meta", "t")) S.meta = fn() end extensions.a = function(S,text) local url, ni = text:match("(%S+)%s*()") local body = prose(S, text:sub(ni)) return T.a({href=url},body) end extensions.def = function(S,text,name) if extensions[name] then io.stderr:write("warning: redefining extension "..name.."\n") end local fn = assert(load( string.format("return %s", text), "def "..name, "t", setmetatable({S=S, T=T, html=html, prose=prose}, {__index=_G}) )) extensions[name]=fn() end setmetatable(extensions, {__index = function(t, k) return function(S, text) io.stderr:write("warning: unknown extension "..k.."\n") return text end end}) return prose