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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
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
|