1--[[
2	Licensed according to the included 'LICENSE' document
3	Author: Thomas Harning Jr <harningt@gmail.com>
4]]
5local setmetatable = setmetatable
6local assert, loadstring = assert, loadstring or load
7
8local _ENV = nil
9
10-- Key == weak, if main key goes away, then cache cleared
11local outputCache = setmetatable({}, {__mode = 'k'})
12-- TODO: inner tables weak?
13
14local function buildFunction(nextValues, innerValue, valueWriter, innerWriter)
15	local putInner = ""
16	if innerValue and innerWriter then
17		-- Prepare the lua-string representation of the separator to put in between values
18		local formattedInnerValue = ("%q"):format(innerValue)
19		-- Fill in the condition %WRITE_INNER% and the %INNER_VALUE% to actually write
20		putInner = innerWriter:gsub("%%WRITE_INNER%%", "%%1"):gsub("%%INNER_VALUE%%", formattedInnerValue)
21	end
22	-- Template-in the value writer (if present) and its conditional argument
23	local functionCode = nextValues:gsub("PUTINNER(%b())", putInner)
24	-- %VALUE% is to be filled in by the value-to-write
25	valueWriter = valueWriter:gsub("%%VALUE%%", "%%1")
26	-- Template-in the value writer with its argument
27	functionCode = functionCode:gsub("PUTVALUE(%b())", valueWriter)
28	functionCode = [[
29		return function(composite, ret, encode, state)
30	]] .. functionCode .. [[
31		end
32	]]
33	return assert(loadstring(functionCode))()
34end
35
36local function prepareEncoder(cacheKey, nextValues, innerValue, valueWriter, innerWriter)
37	local cache = outputCache[cacheKey]
38	if not cache then
39		cache = {}
40		outputCache[cacheKey] = cache
41	end
42	local fun = cache[nextValues]
43	if not fun then
44		fun = buildFunction(nextValues, innerValue, valueWriter, innerWriter)
45		cache[nextValues] = fun
46	end
47	return fun
48end
49
50local output_utility = {
51	prepareEncoder = prepareEncoder
52}
53
54return output_utility
55