1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
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 extension(str)
local verb, rest = str:match("^{(%w+);%s*(.*)}$")
if verb then
return emplace(extensions[verb](S, rest))
end
local verbonly = str:match("^{(%w+)}$")
if verbonly then
return emplace(extensions[verbonly](S))
end
error("invalid extension syntax: "..str)
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
setmetatable(extensions, {__index = function(t, k)
return function(S, text)
io.stderr:write("warning: unknown extension "..k.."\n")
return text
end
end})
return prose
|