parsav  tpl.t at [419d1a1ebe]

File tpl.t artifact ad44dd6129 part of check-in 419d1a1ebe


-- vim: ft=terra
-- string template generator:
-- returns a function that fills out a template
-- with the strings given

local util = dofile 'common.lua'
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 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','')
	for start, 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
			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

	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 = {}
		for afterseg,key in pairs(fields) do
			if not kfac[key] then
				rec.entries[#rec.entries + 1] = {
					field = key;
					type = rawstring;
				}
			end
			kfac[key] = (kfac[key] or 0) + 1
		end
		for key, fac in pairs(kfac) do
			tallyup[#tallyup + 1] = quote
				[runningtally] = [runningtally] + lib.str.sz([symself].[key])*fac
			end
		end
	end

	local copiers = {}
	local senders = {}
	local symtxt = symbol(lib.mem.ptr(int8))
	local cpypos = symbol(&opaque)
	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
		if fields[idx] then
			copiers[#copiers+1] = quote
				[cpypos] = lib.mem.cpy([cpypos],
					[&opaque](symself.[fields[idx]]),
					lib.str.sz(symself.[fields[idx]]))
			end
			senders[#senders+1] = quote
				lib.net.mg_send([destcon],
					symself.[fields[idx]],
					lib.str.sz(symself.[fields[idx]]))
			end
		end
	end

	local tid = tplspec.id or '<anonymous>'
	rec.methods.tostr = terra([symself])
		lib.dbg(['compiling template ' .. tid])
		[tallyup]
		var [symtxt] = lib.mem.heapa(int8, [runningtally])
		var [cpypos] = [&opaque](symtxt.ptr)
		[copiers]
		@[&int8](cpypos) = 0
		return symtxt
	end
	rec.methods.send = terra([symself], [destcon], code: uint16, hd: lib.mem.ptr(lib.http.header))
		lib.dbg(['transmitting template ' .. 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)
		[senders]
		lib.net.mg_send([destcon],'\r\n',2)
	end

	return rec
end

return m