跳转到内容

模組:Template utilities

維基詞典,自由的多語言詞典


local export = {}

export.brackets_temp = { ['{{'] = '}}' }
export.brackets_temp_and_link = { ['{{'] = '}}', ['%[%['] = ']]' }

function export.find_bracket(str, brackets, p_start)
	local function find_left(pos_start)
		local p1_result, cap_result, right_result = str:len() + 1
		
		local cap_this
		for k, v in pairs(brackets) do
			cap_this = {str:find(k, pos_start)}
			if cap_this[1] and cap_this[1] < p1_result then
				p1_result, cap_result, right_result = cap_this[1], cap_this, v
			end
		end
		if not cap_result then return nil end
		
		local p2_result = cap_result[2]
		cap_result[2] = str:sub(p1_result, p2_result)
		local t = type(right_result)
		if t == 'string' then
			return p1_result, p2_result, right_result:gsub('%%(.)', function(m1)
				if m1:match'%d' then
					return cap_result[m1 + 2]
				else return m1 end
			end)
		end
		
		local repl
		if t == 'function' then
			cap_result[3] = cap_result[3] or cap_result[2]
			repl = right_result(select(3, unpack(cap_result)))
		elseif t == 'table' then
			repl = right_result[cap_result[3] or cap_result[2]]
		else error('bad right bracket type: ' .. t) end
		
		if not repl then repl = cap_result[2]
		elseif type(repl) == 'number' then repl = tostring(repl) end
		return p1_result, p2_result, repl
	end
	
	local p_init, p0, str_brac = find_left(p_start)
	if p_init == nil then return nil end
	local nest = {str_brac}
	
	local p1, p2, p3, p4
	repeat
		p0 = p0 + 1
		p1, p2 = str:find(str_brac, p0)
		if p1 == nil then return nil end
		if p1 > p2 then error'Any bracket must not have zero length.' end
		p3, p4, str_brac = find_left(p0)
		if p3 == nil then
			local n = #nest - 1
			while n > 0 do
				p1, p2 = str:find(nest[n], p2 + 1)
				if p1 == nil then return nil end
				n = n - 1
			end
			p0 = p2
			break
		else
			if p3 > p4 then error'Any bracket must not have zero length.' end 
			if p3 < p1 then
				table.insert(nest, str_brac)
				p0 = p4
			else
				table.remove(nest)
				str_brac = nest[#nest]
				p0 = p2
			end
		end
	until #nest == 0
	
	return p_init, p0, str:sub(p_init, p0)
end

function export.gfind_bracket(str, brackets)
    local p0 = 0
    return function()
		p0 = p0 + 1
        local p1, p2, text_b = export.find_bracket(str, brackets, p0)
        p0 = p2
        return p1, p2, text_b
    end
end

function export.find_ignoring_brackets(str, brackets, pat, init, ...)
    local find_result = {str:find(pat, init, ...)}
    local p1, p2 = find_result[1], find_result[2]
    if p1 == nil then return nil end
	
    local p3, p4 = export.find_bracket(str, brackets)
	while p4 and p4 <= p2 do p3, p4 = export.find_bracket(str, brackets, p4 + 1) end

	while p3 and p3 <= p2 do
        find_result = {str:find(pat, p4 + 1, ...)}
		p1, p2 = find_result[1], find_result[2]
        if p1 == nil then return nil end
		
        while p4 and p4 <= p2 do p3, p4 = export.find_bracket(str, brackets, p4 + 1) end
    end
    return unpack(find_result)
end

function export.gsplit_ignoring_brackets(str, brackets, sep)
	local p0 = 0
	local empty = 0
	return function()
		if p0 == nil then return nil end
		p0 = p0 + empty 
		if p0 > str:len() then return nil end
		p0 = p0 + 1
		local p1, p2 = export.find_ignoring_brackets(str, brackets, sep, p0)
		p0, p2 = p2, p0 - empty
		if p1 then
			empty =  p1 > p0 and 1 or 0
			return str:sub(p2, p1 - 1)
		else return str:sub(p2) end
	end
end

function export.parse_temp(str)
    if str:sub(1, 2) ~= '{{' or str:sub(-2) ~= '}}' then return nil end
    str = str:sub(3, -3)
	
    local p_title_end = export.find_ignoring_brackets(str, export.brackets_temp_and_link, '|')
    if not p_title_end then return { title = str, args = {} }
	end

	local args = {}
    local count = 0
    for arg in export.gsplit_ignoring_brackets(str:sub(p_title_end + 1), export.brackets_temp_and_link, '|') do
        local p_eqsign = export.find_ignoring_brackets(arg, export.brackets_temp_and_link, '=')
        if p_eqsign then
			local arg_name = arg:sub(1, p_eqsign - 1)
			local arg_name_num = tonumber(arg_name)
			if arg_name_num and arg_name_num > 0 and arg_name_num == math.floor(arg_name_num) then
				arg_name = arg_name_num
			else
				arg_name = arg_name:match'^%s*(%S-)%s*$'
			end
            args[arg_name] = arg:sub(p_eqsign + 1):match'^%s*(%S-)%s*$'
        else
			count = count + 1
            args[count] = arg
        end
    end

    return { title = str:sub(1, p_title_end - 1), args = args }
end

function export.iter_num(t)
	local i = 0
	local index_max = 0
	for k, _ in pairs(t.args) do
		if type(k) == 'number' and index_max < k then
			index_max = k
		end
	end
	return function()
		local v
		repeat
			i = i + 1
			if i > index_max then return nil end
			v = t.args[i]
		until v
		return i, v
	end
end

function export.glue_temp(t)
	local content = { t.title }
	for i, v in export.iter_num(t) do
		if i == #content then
			table.insert(content, v)
		else
			table.insert(content, i .. '=' .. v)
		end
	end
	for k, v in pairs(t.args) do
		if type(k) == 'string' then
			table.insert(content, k .. '=' .. v)
		end
	end
	return '{{' .. table.concat(content, '|') .. '}}'
end

return export