Module:epto

From Wiktionary, the free dictionary
Jump to navigation Jump to search

"epto" stands for Example Plain Text Output. It displays the output of a template or module function in plain text.

Example:

{{#invoke:epto|lb|en|archaic|neologism|chess}}

{{#invoke:epto|mod:en-headword#show/nouns|pagename=lua error|-|+}}

{{#invoke:epto|mod:grc-utilities#tokenize("ἡμεῖς")}}


local m_tu = require'Module:template utilities'

local function codeWrap(frame, s1, s2, lang)
	return
		'<div class="NavFrame" style="border:0px;">' ..
			'<div class="NavHead" style="font-size:105%; border:1px solid #aaaaaa; margin-left:-1px; background-color:#CCCCFF; " cellpadding="3">' ..
				'Plain text output of ' .. frame:extensionTag({name = 'nowiki', content = s1}) ..
			'</div>' .. -- style="border: 1px solid; background-color: #f8f8f8; padding: 0.5em; "
			'<div class="NavContent" align="left" style="border: 1px solid; padding: 0em 1em; ">' ..
				frame:extensionTag({name = 'syntaxhighlight', args = {lang = lang}, content = s2}) ..
			'</div>' ..
		'</div>'
end

local function parse_lua_parameter(str) -- since Scribunto disabled loadstring()
	local t = tonumber(str)
	if t then return number end
	
	if str:match'^".*"$' or str:match"^'.*'$" then
		return str:sub(2, -2)
	end
	t = str:match'^%[(=*)%[.*]%1]$'
	if t then
		t = t:len() + 3
		return str:sub(t, -t)
	end
	
	if str == 'true' then return true end
	if str == 'false' then return false end
	if str == 'nil' then return nil end
	
	if str:match'^[_%a][_%w%.]*$' then
		t = _G
		for id in str:gmatch'.*%f[%.%z]' do
			if id:match'^[_%a][_%w]*$' then
				t = t[id]
			else error('invalid identifier: ' .. id) end
		end
		return t
	end
	
	t = str:match'^%b{}$'
	if t then
		local r, r_count = {}, 0
		for item in m_tu.gsplit_ignoring_brackets(t:sub(2, -2), {['{'] = '}'}, ',') do
			local key, value = item:match'^%s*%[%s*(.-)%s]%s*=%s*(.-)%s*$'
			if key and key:len() > 0 and value:len() > 0 then
				r[parse_lua_parameter(key)] = parse_lua_parameter(value)
			else
				r_count = r_count + 1
				r[r_count] = parse_lua_parameter(item:match'^%s*(.-)%s*$')
			end
		end
		return r
	end
	
	mw.log(str)
	error('unsupported input: "' .. str .. '"')
end

local function push_frame(title_and_args)
	local frame_this = mw.getCurrentFrame()
	local frame_child = frame_this:newChild(title_and_args)
	function frame_child:getParent() return frame_this end
	function mw.getCurrentFrame() return frame_child end
	return frame_child
end

local function pop_frame()
	local frame_parent = mw.getCurrentFrame():getParent()
	function mw.getCurrentFrame() return frame_parent end
	return frame_parent
end

local function mod(frame, mod_name, func_name, extra_mod_args)
	local s_args = extra_mod_args:match'^%b()'
	if s_args then
		local func_args = {}
		local len_func_args = 0
		for a in s_args:sub(2, -2):gmatch'(.-)%f[,%z],?' do
			len_func_args = len_func_args + 1
			if a ~= '' then func_args[len_func_args] = parse_lua_parameter(a) end
		end
		return codeWrap(frame, 'require("Module:' .. mod_name .. '").' .. func_name .. s_args, mw.dumpObject(require('Module:' .. mod_name)[func_name](unpack(func_args, 1, len_func_args))), 'lua')
	else
		local s_args_invoke = extra_mod_args:sub(1, 1) == '/' and extra_mod_args:gsub('/', '|') or ''
		return codeWrap(frame, '{{#invoke:' .. mod_name .. '|' .. func_name .. s_args_invoke .. '}} with parent template ' .. m_tu.glue_temp{
			title = '...',
			args = frame.args,
		}, tostring(require('Module:' .. mod_name)[func_name](push_frame(m_tu.parse_temp('{{Module:' .. mod_name .. s_args_invoke .. '}}')))), 'html')
	end
end

local function t_subst_recur(frame, temp_name)
	local success, s = pcall(frame.callParserFunction, frame, { name = temp_name, args = frame.args })
	if not success then
		success, s = pcall(frame.expandTemplate, frame, { title = temp_name, args = frame.args })
		if not success then
			return m_tu.glue_temp{ title = temp_name, args = frame.args }
		end
	end
	
	
	local replacements = {}
	for p1, p2, s_invoke in m_tu.gfind_bracket(s, m_tu.brackets_temp) do
		s_invoke = s_invoke:gsub('^{{subst:', '{{')
		
		local mod_name, func_name, mod_args = s_invoke:sub(3, -3):match'^#invoke:(..-)|(..-)%f[%z|](|?.*)'
		if mod_name then
			table.insert(replacements, {
				p1,
				p2,
				tostring(require('Module:' .. mod_name)[func_name](push_frame(m_tu.parse_temp('{{Module:' .. mod_name .. mod_args .. '}}').args)))
			})
			pop_frame()
		else
			local invoke = m_tu.parse_temp(s_invoke)
			invoke.title = mw.title.new(invoke.title, "Template") or invoke.title
			table.insert(replacements, {p1, p2, t_subst_recur(push_frame(invoke), invoke.title.prefixedText)})
			pop_frame()
		end
	end
	
	for i = #replacements, 1, -1 do
		s = s:sub(1, replacements[i][1] - 1) .. replacements[i][3] .. s:sub(replacements[i][2] + 1)
	end
	return s
end

local function t_subst(frame, temp_name)
	local t = { title = 'subst:' .. temp_name , args = frame.args }
	return codeWrap(frame, m_tu.glue_temp(t), t_subst_recur(frame, temp_name), 'html')
end

local function t(frame, temp_name)
	local t = { title = temp_name , args = frame.args }
	return codeWrap(frame, m_tu.glue_temp(t), frame:expandTemplate(t), 'html')
end

return setmetatable({}, {
	__index = function(self, str)
		local subst = str:match'^subst:'
		if subst then str = str:sub(7) end
		
		local mod_name, func_name, extra_mod_args = str:match'^#invoke:(..-)#(..-)%f[%z/(](.*)' 
		if not mod_name then
			mod_name, func_name, extra_mod_args = str:match'^[Mm]odule:(..-)#(..-)%f[%z/(](.*)'
		end
		if not mod_name then
			mod_name, func_name, extra_mod_args = str:match'^[Mm]od:(..-)#(..-)%f[%z/(](.*)'
		end
		if mod_name then
			return function(frame)
				return mod(frame, mod_name, func_name, extra_mod_args)
			end
		end
		if mod_name then
			return function(frame)
				return mod(mod_name, func_name, frame)
			end
		end
		
		if subst then
			return function(frame)
				return t_subst(frame, str)
			end
		else
			return function(frame)
				return t(frame, str)
			end
		end
	end,
})