parsav  parsav.t at [59e1d7d56a]

File parsav.t artifact ce122c09dc part of check-in 59e1d7d56a


-- vim: ft=terra

local util = dofile('common.lua')
local buildopts, buildargs = util.parseargs{...}
config = dofile('config.lua')

lib = {
	init = {};
	loadlib = function(name,hdr)
		local p = config.pkg[name]
		-- for _,v in pairs(p.dylibs) do
		-- 	terralib.linklibrary(p.libdir .. '/' .. v)
		-- end
		return terralib.includec(p.incdir .. '/' .. hdr)
	end;
	dispatch = function(tbl)
		return macro(function(v,...)
			for ty,fn in pairs(tbl) do
				if v.tree.type == ty then return fn(v,...) end
			end
			return (tbl[false])(v,...)
		end)
	end;
	emit = function(...)
		local code = {}
		for i,v in ipairs{...} do
			if type(v) == 'string' or type(v) == 'number' then
				local str = tostring(v)
				code[#code+1] = `lib.io.send(2, str, [#str])
			elseif v.tree:is 'constant' then
				local str = tostring(v:asvalue())
				code[#code+1] = `lib.io.send(2, str, [#str])
			else
				code[#code+1] = quote var n = v in
					lib.io.send(2, n, lib.str.sz(n)) end
			end
		end
		code[#code+1] = `lib.io.send(2, '\n', 1)
		return code
	end;
	trn = macro(function(cond, i, e)
		return quote
			var c: bool = [cond]
			var r: i.tree.type
			if c == true then r = i else r = e end
		in r end
	end);
	proc = {
		exit = terralib.externfunction('exit', int -> {});
		getenv = terralib.externfunction('getenv', rawstring -> rawstring);
	};
	io = {
		send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
		recv = terralib.externfunction('read',  {int, rawstring, intptr} -> ptrdiff);
		say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
		fmt = terralib.externfunction('printf',
			terralib.types.funcpointer({rawstring},{int},true));
	};
	str = {
		sz = terralib.externfunction('strlen', rawstring -> intptr);
		cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
		ncmp = terralib.externfunction('strncmp', {rawstring, rawstring, intptr} -> int);
		cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
		ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
		ndup = terralib.externfunction('strndup',{rawstring, intptr} -> rawstring);
		fmt = terralib.externfunction('asprintf',
			terralib.types.funcpointer({&rawstring},{int},true));
	};
	copy = function(tbl)
		local new = {}
		for k,v in pairs(tbl) do new[k] = v end
		setmetatable(new, getmetatable(tbl))
		return new
	end;
	mem = {
		zero = macro(function(r)
			return quote
				for i = 0, [r.tree.type.N] do r[i] = 0 end
			end
		end);
		heapa_raw = terralib.externfunction('malloc', intptr -> &opaque);
		heapr_raw = terralib.externfunction('realloc', {&opaque, intptr} -> &opaque);
		heapf = terralib.externfunction('free', &opaque -> {});
		cpy = terralib.externfunction('mempcpy',{&opaque, &opaque, intptr} -> &opaque);
		heapa = macro(function(ty, sz)
			local p = lib.mem.ptr(ty:astype())
			return `p {
				ptr = [&ty:astype()](lib.mem.heapa_raw(sizeof(ty) * sz));
				ct = sz;
			}
		end)
	};
}

local noise = global(uint8,1)
local noise_header = function(code,txt,mod)
	if mod then
		return string.format('\27[%s;1m(parsav::%s %s)\27[m ', code,mod,txt)
	else
		return string.format('\27[%s;1m(parsav %s)\27[m ', code,txt)
	end
end
local defrep = function(level,n,code)
	return macro(function(...)
		local q = lib.emit(noise_header(code,n), ...)
		return quote
			if noise >= level then [q] end
		end
	end);
end
lib.dbg = defrep(3,'debug', '32')
lib.report = defrep(2,'info', '35')
lib.warn = defrep(1,'warn', '33')
lib.bail = macro(function(...)
	local q = lib.emit(noise_header('31','fatal'), ...)
	return quote
		[q]
		lib.proc.exit(1)
	end
end);
lib.stat = terralib.memoize(function(ty)
	local n = struct {
		ok: bool
		union {
			error: uint8
			val: ty
		}
	}
	n.name = string.format("stat<%s>", ty.name)
	n.stat_basetype = ty
	return n
end)
lib.enum = function(tbl)
	local ty = uint8
	if #tbl >= 2^32 then ty = uint64 -- hey, can't be too safe
	elseif #tbl >= 2^16 then ty = uint32
	elseif #tbl >= 2^8 then ty = uint16 end
	local o = { t = ty }
	for i, name in ipairs(tbl) do
		o[name] = i
	end
	return o
end
lib.mem.ptr = terralib.memoize(function(ty)
	local t = terralib.types.newstruct(string.format('ptr<%s>', ty))
	t.entries = {
		{'ptr', &ty};
		{'ct', intptr};
	}
	t.ptr_basetype = ty
	local recurse = false
	if ty:isstruct() then
		if ty.methods.free then recurse = true end
	end
	t.methods = {
		free = terra(self: &t): bool
			[recurse and quote
				self.ptr:free()
			end or {}]
			if self.ct > 0 then
				lib.mem.heapf(self.ptr)
				self.ct = 0
				return true
			end
			return false
		end;
		init = terra(self: &t, newct: intptr): bool
			var nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
			if nv ~= nil then
				self.ptr = nv
				self.ct = newct
				return true
			else return false end
		end;
		resize = terra(self: &t, newct: intptr): bool
			var nv: &ty
			if self.ct > 0
				then nv = [&ty](lib.mem.heapr_raw(self.ptr, sizeof(ty) * newct))
				else nv = [&ty](lib.mem.heapa_raw(sizeof(ty) * newct))
			end
			if nv ~= nil then
				self.ptr = nv
				self.ct = newct
				return true
			else return false end
		end;
	}
	return t
end)
lib.mem.vec = terralib.memoize(function(ty)
	local v = terralib.types.newstruct(string.format('vec<%s>', ty.name))
	v.entries = {
		{field = 'storage', type = lib.mem.ptr(ty)};
		{field = 'sz', type = intptr};
		{field = 'run', type = intptr};
	}
	local terra biggest(a: intptr, b: intptr)
		if a > b then return a else return b end
	end
	terra v:assure(n: intptr)
		if self.storage.ct < n then
			self.storage:resize(biggest(n, self.storage.ct + self.run))
		end
	end
	v.methods = {
		init = terra(self: &v, run: intptr): bool
			if not self.storage:init(run) then return false end
			self.run = run
			self.sz = 0
			return true
		end;
		new = terra(self: &v): &ty
			self:assure(self.sz + 1)
			self.sz = self.sz + 1
			return self.storage.ptr + (self.sz - 1)
		end;
		push = terra(self: &v, val: ty)
			self:assure(self.sz + 1)
			self.storage.ptr[self.sz] = val
			self.sz = self.sz + 1
		end;
		free = terra(self: &v) self.storage:free() end;
		last = terra(self: &v, idx: intptr): &ty
			if self.sz > idx then
				return self.storage.ptr + (self.sz - (idx+1))
			else lib.bail('vector underrun!') end
		end;
		crush = terra(self: &v)
			self.storage:resize(self.sz)
			return self.storage
		end;
	}
	v.metamethods.__apply = terra(self: &v, idx: intptr): &ty -- no index??
		if self.sz > idx then
			return self.storage.ptr + idx
		else lib.bail('vector overrun!') end
	end
	return v 
end)

lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.file = terralib.loadfile('file.t')()
lib.math = terralib.loadfile('math.t')()
lib.crypt = terralib.loadfile('crypt.t')()
lib.http = terralib.loadfile('http.t')()
lib.tpl = terralib.loadfile('tpl.t')()
lib.string = terralib.loadfile('string.t')()
lib.store = terralib.loadfile('store.t')()

local be = {}
for _, b in pairs { 'pgsql' } do
	be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')()
end
lib.store.backends = global(`array([be]))

lib.cmdparse = terralib.loadfile('cmdparse.t')()
lib.srv = terralib.loadfile('srv.t')()

do local collate = function(path,f, ...)
	return loadfile(path..'/'..f..'.lua')(path, ...)
end
data = {
	view = collate('view','load');
} end

-- slightly silly -- because we're using plain lua to gather up
-- the template sources, they have to be actually turned into
-- templates in the terra code, so we "mutate" them here
for k,v in pairs(data.view) do
	local t = lib.tpl.mk { body = v, id = 'view/'..k }
	data.view[k] = t
end

local pemdump = macro(function(pub, kp)
	local msg = (pub:asvalue() and ' * emitting public key\n') or ' * emitting private key\n'
	return quote
		var buf: lib.crypt.pemfile
		lib.mem.zero(buf)
		lib.crypt.pem(pub, &kp, buf)
		lib.io.send(1, msg, [#msg])
		lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
		lib.io.send(1, '\n', 1)
	end
end)

do
	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
	terra version() lib.io.send(1, p, [#p]) end
end
terra noise_init()
	var n = lib.proc.getenv('parsav_noise')
	if n ~= nil then
		if n[0] >= 0x30 and n[0] <= 0x39 and n[1] == 0 then
			noise = n[0] - 0x30
			return
		end
	end
	noise = 1
end

local options = lib.cmdparse {
	version = {'V', 'display information about the binary build and exit'};
	quiet = {'q', 'do not print to standard out'};
	help = {'h', 'display this list'}
}

terra entry(argc: int, argv: &rawstring): int
	noise_init()
	[lib.init]

	-- shut mongoose the fuck up
	lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil)

	var mode: options
	mode:parse(argc,argv)
	if mode.version then
		version()
		return 0
	end
	if mode.help then
		lib.io.send(1,  [options.helptxt], [#options.helptxt])
		return 0
	end
	var srv: lib.srv
	srv:start('backend.conf')
	lib.report('listening for requests')
	while true do
		srv:poll()
	end
	srv:shutdown()

	return 0
end

local bflag = function(long,short)
	if short and util.has(buildopts, short) then return true end
	if long and util.has(buildopts, long) then return true end
	return false
end

if bflag('dump-config','C') then
	print(util.dump(config))
	os.exit(0)
end

local emit = print
if bflag('quiet','q') then emit = function() end end

local out = config.exe and 'parsav' or 'parsav.o'
local linkargs = {}
if config.posix then
	linkargs[#linkargs+1] = '-pthread'
end
for _,p in pairs(config.pkg) do util.append(linkargs, p.linkargs) end
emit('linking with args',util.dump(linkargs))
terralib.saveobj(out, {
		main = entry
	},
	linkargs,
	config.tgttrip and terralib.newtarget {
		Triple = config.tgttrip;
		CPU = config.tgtcpu;
		FloatABIHard = config.tgthf;
	} or nil)