Documentation for this module may be created at Modulus:Path/doc

function split_path (path)
	local parts = {}
	local len = 0
	for token in path:gmatch("[^/]+") do
		len = len + 1
		parts[len] = token
	end
	return parts, len
end


function get_abs_path (current, page)
	local tbl_i, len_i = split_path(page)
	local tbl_o
	local len_o
	if tbl_i[1] == '.' or tbl_i[1] == '..' then
		tbl_o, len_o = split_path(current)
	else
		tbl_i[1] = tbl_i[1]:gsub('^([^:]?)([^:]*)(:?)(.?)',
			function (s1, s2, s3, s4)
				if s3 == '' then return s1:upper() .. s2 end
				return s1:upper() .. s2 .. s3 .. s4:upper()
			end,
			1
		)
		tbl_o = {}
		len_o = 0
	end
	for key, val in ipairs(tbl_i) do
		if val == '..' then
			if len_o < 1 then
				error('Module:Path: invalid path', 0) end
			tbl_o[len_o] = nil
			len_o = len_o - 1
		elseif val ~= '.' then
			len_o = len_o + 1
			tbl_o[len_o] = val
		end
	end
	if len_o < 1 then
		tbl_o[1] = ''
		len_o = 1
	end
	return tbl_o, len_o
end


function get_rel_path (current, page, add_prefixes)
	local tbl_i, len_i = get_abs_path(current, page)
	local tbl_c, len_c = split_path(current)
	local tbl_o = {}
	local len_o = 0
	local minlen
	local new_at = 0
	if len_c < len_i then minlen = len_c else minlen = len_i end
	for idx = 1, minlen do
		if tbl_c[idx] ~= tbl_i[idx] then
			new_at = idx
			break
		end
	end
	if new_at == 1 then return table.concat(tbl_i, '/') end
	if add_prefixes then
		if new_at == 0 then
			tbl_o[1] = '.'
			new_at = minlen + 1
		end
		for idx = new_at, len_c do
			len_o = len_o + 1
			tbl_o[len_o] = '..'
		end
		if len_o < 1 then len_o = 1 end
	elseif new_at == 0 then new_at = minlen + 1 end
	for idx = new_at, len_i do
		len_o = len_o + 1
		tbl_o[len_o] = tbl_i[idx]
	end
	return table.concat(tbl_o, '/')
end


local iface = {}


iface.abs = function (frame)
	local page = frame.args[1]
	if page ~= nil then page = page:match'^%s*(.*%S)' end
	if page == nil then return mw.title.getCurrentTitle().prefixedText end
	local retval, _ = table.concat(get_abs_path(
		mw.title.getCurrentTitle().prefixedText,
		page:gsub('^%s*/%s*', './', 1)
	), '/')
	return retval
end


iface.rel = function (frame)
	local page = frame.args[1]
	if page ~= nil then page = page:match'^%s*(.*%S)' end
	if page == nil then return '.' end
	return get_rel_path(mw.title.getCurrentTitle().prefixedText,
		page:gsub('^%s*/%s*', './', 1), true)
end


iface.sub = function (frame)
	local page = frame.args[1]
	if page ~= nil then page = page:match'^%s*(.*%S)' end
	if page == nil then return '' end
	return get_rel_path(mw.title.getCurrentTitle().prefixedText,
		page:gsub('^%s*/%s*', './', 1), false)
end


return iface