-- vim: ft=terra
-- string template generator:
-- returns a function that fills out a template
-- with the strings given
local util = lib.util
local pstr = lib.mem.ptr(int8)
local m = {}
function m.mk(tplspec)
local str
if type(tplspec) == 'string'
then str = tplspec tplspec = {}
else str = tplspec.body
end
local tplchar_o = tplspec.sigil or '@'
local tplchar = tplchar_o
local magic = {
['%'] = true, ['$'] = true, ['^'] = true;
['.'] = true, ['*'] = true, ['+'] = true;
['-'] = true, ['?'] = true;
}
tplchar_o = string.gsub(tplchar_o,'%%','%%%%')
tplchar = string.gsub(tplchar, '.', function(c)
if magic[c] then return '%' .. c end
end)
local last = 1
local fields = {}
local segs = {}
local docs = {}
local constlen = 0
-- strip out all irrelevant whitespace to tidy things up
-- TODO: find way to exclude <pre> tags?
str = str:gsub('[\n^]%s+','')
str = str:gsub('%s+[\n$]','')
str = str:gsub('\n','')
str = str:gsub('</a><a ','</a> <a ') -- keep nav links from getting smooshed
str = str:gsub(tplchar .. '%?([-%w]+)', function(file)
if not docs[file] then docs[file] = data.doc[file] end
return string.format('<a href="#help-%s" class="help">?</a>', file)
end)
for start, mode, key, stop in string.gmatch(str,'()'..tplchar..'([:!]?)(%w+)()') do
if string.sub(str,start-1,start-1) ~= '\\' then
segs[#segs+1] = string.sub(str,last,start-1)
fields[#segs] = { key = key, mode = (mode ~= '' and mode or nil) }
last = stop
end
end
segs[#segs+1] = string.sub(str,last)
for i, s in ipairs(segs) do
segs[i] = string.gsub(s, '\\'..tplchar, tplchar_o)
constlen = constlen + string.len(segs[i])
end
for n,d in pairs(docs) do
local html = string.format(
'<div id="help-%s" class="modal"> <a href="#0">close</a> <div>%s</div></div>', n, d.text
)
segs[#segs] = segs[#segs] .. html
constlen = constlen + #html
end
local runningtally = symbol(intptr)
local tallyup = {quote
var [runningtally] = 1 + constlen
end}
local rec = terralib.types.newstruct(string.format('template<%s>',tplspec.id or ''))
local symself = symbol(&rec)
do local kfac = {}
local sanmode = {}
for afterseg,fld in ipairs(fields) do
if not kfac[fld.key] then
rec.entries[#rec.entries + 1] = {
field = fld.key;
type = lib.mem.ptr(int8);
}
end
kfac[fld.key] = (kfac[fld.key] or 0) + 1
sanmode[fld.key] = fld.mode == ':' and 6 or fld.mode == '!' and 5 or 1
end
for key, fac in pairs(kfac) do
local sanfac = sanmode[key]
tallyup[#tallyup + 1] = quote
[runningtally] = [runningtally] + ([symself].[key].ct)*fac*sanfac
end
end
end
local copiers = {}
local senders = {}
local appenders = {}
local symtxt = symbol(lib.mem.ptr(int8))
local cpypos = symbol(&opaque)
local pool = symbol(&lib.mem.pool)
local accumulator = symbol(&lib.str.acc)
local destcon = symbol(&lib.net.mg_connection)
for idx, seg in ipairs(segs) do
copiers[#copiers+1] = quote [cpypos] = lib.mem.cpy([cpypos], [&opaque]([seg]), [#seg]) end
senders[#senders+1] = quote lib.net.mg_send([destcon], [seg], [#seg]) end
appenders[#appenders+1] = quote [accumulator]:push([seg], [#seg]) end
if fields[idx] and fields[idx].mode then
local f = fields[idx]
local fp = `symself.[f.key]
copiers[#copiers+1] = quote
if fp.ct > 0 then
var san = lib.html.sanitize(pool, fp, [f.mode == ':'])
[cpypos] = lib.mem.cpy([cpypos], [&opaque](san.ptr), san.ct)
--san:free()
end
end
senders[#senders+1] = quote
if fp.ct > 0 then
var san = lib.html.sanitize(pool, fp, [f.mode == ':'])
lib.net.mg_send([destcon], san.ptr, san.ct)
--san:free()
end
end
appenders[#appenders+1] = quote
if fp.ct > 0 then
var san = lib.html.sanitize(pool, fp, [f.mode == ':'])
[accumulator]:ppush(san)
--san:free()
end
end
elseif fields[idx] then
local f = fields[idx]
local fp = `symself.[f.key]
copiers[#copiers+1] = quote
if fp.ct > 0 then
[cpypos] = lib.mem.cpy([cpypos], [&opaque](fp.ptr), fp.ct)
end
end
senders[#senders+1] = quote
if fp.ct > 0 then
lib.net.mg_send([destcon], fp.ptr, fp.ct)
end
end
appenders[#appenders+1] = quote
if fp.ct > 0 then [accumulator]:ppush(fp) end
end
end
end
local tid = tplspec.id or '<anonymous>'
rec.methods.poolstr = terra([symself],[pool])
lib.dbg(['pooling template ' .. tid])
[tallyup]
var [symtxt] = pool:alloc(int8, [runningtally])
var [cpypos] = [&opaque](symtxt.ptr)
[copiers]
@[&int8](cpypos) = 0
symtxt.ct = [&int8](cpypos) - symtxt.ptr
return symtxt
end
rec.methods.tostr = terra([symself])
lib.dbg(['compiling template ' .. tid])
[tallyup]
var [symtxt] = lib.mem.heapa(int8, [runningtally])
var [cpypos] = [&opaque](symtxt.ptr)
var p: lib.mem.pool p:init(128)
var [pool] = &p
[copiers]
@[&int8](cpypos) = 0
symtxt.ct = [&int8](cpypos) - symtxt.ptr
pool:free()
return symtxt
end
rec.methods.append = terra([symself], [accumulator])
var [pool]
var p: lib.mem.pool
if [accumulator].pool == nil then
p:init(128)
pool = &p
else pool = [accumulator].pool end
lib.dbg(['appending template ' .. tid])
[tallyup]
accumulator:cue([runningtally])
[appenders]
if [accumulator].pool == nil then p:free() end
return accumulator
end
rec.methods.head = terra([symself], [destcon], code: uint16, hd: lib.mem.ptr(lib.http.header))
var p: lib.mem.pool p:init(128) -- FIXME
var [pool] = &p
lib.dbg(['transmitting template headers ' .. tid])
[tallyup]
lib.net.mg_printf([destcon], 'HTTP/1.1 %s', lib.http.codestr(code))
for i = 0, hd.ct do
lib.net.mg_printf([destcon], '%s: %s\r\n', hd.ptr[i].key, hd.ptr[i].value)
end
lib.net.mg_printf([destcon],'Content-Length: %llu\r\n\r\n', [runningtally] + 1)
p:free()
end
rec.methods.send = terra([symself], [destcon], code: uint16, hd: lib.mem.ptr(lib.http.header))
var p: lib.mem.pool p:init(128) -- FIXME
var [pool] = &p
lib.dbg(['transmitting template ' .. tid])
symself:head(destcon,code,hd)
[senders]
lib.net.mg_send([destcon],'\r\n',2)
p:free()
end
rec.methods.sz = terra([symself])
lib.dbg(['tallying template ' .. tid])
[tallyup]
return [runningtally] + 1
end
return rec
end
return m