Module:LuaCall: Difference between revisions

From Test Wiki
Jump to navigation Jump to search
This module is complicated enough as is. You don't need to add even more tangential functions, when p.call was working perfectly fine for months
Note: Since imported from Wikipedia, this is licensed as Creative Commons Attribution-ShareAlike License 4.0.
 
(6 intermediate revisions by 5 users not shown)
Line 1: Line 1:
-- Imported from Wikipedia
-- Licensed as CC BY-SA 4.0
-- See edit history for attribution
local p={}
local p={}


function p.main(frame)
function p.main(frame)
     local parent=frame.getParent(frame) or {}
     local parent = frame.getParent(frame) or {}
     local reserved_value={}
     local reserved_value = {}
     local reserved_function,reserved_contents
     local reserved_function, reserved_contents
     for k,v in pairs(parent.args or {}) do
     for k, v in pairs(parent.args or {}) do
         _G[k]=tonumber(v) or v -- transfer every parameter directly to the global variable table
         _G[k] = tonumber(v) or v -- transfer every parameter directly to the global variable table
     end
     end
     for k,v in pairs(frame.args or {}) do
     for k, v in pairs(frame.args or {}) do
         _G[k]=tonumber(v) or v -- transfer every parameter directly to the global variable table
         _G[k] = tonumber(v) or v -- transfer every parameter directly to the global variable table
     end
     end
     --- Alas Scribunto does NOT implement coroutines, according to
     --- Alas Scribunto does NOT implement coroutines, according to
Line 15: Line 18:
     --- this will not stop us from trying to implement one single lousy function call
     --- this will not stop us from trying to implement one single lousy function call
     if _G[1] then
     if _G[1] then
         reserved_function,reserved_contents=mw.ustring.match(_G[1],"^%s*(%a[^%s%(]*)%(([^%)]*)%)%s*$")
         reserved_function, reserved_contents = mw.ustring.match(_G[1], "^%s*(%a[^%s%(]*)%(([^%)]*)%)%s*$")
     end
     end
     if reserved_contents then
     if reserved_contents then
         local reserved_counter=0
         local reserved_counter = 0
         repeat
         repeat
             reserved_counter=reserved_counter+1
             reserved_counter = reserved_counter + 1
             reserved_value[reserved_counter]=_G[mw.ustring.match(reserved_contents,"([^%,]+)")]
             reserved_value[reserved_counter] = _G[mw.ustring.match(reserved_contents, "([^%,]+)")]
             reserved_contents=mw.ustring.match(reserved_contents,"[^%,]+,(.*)$")
             reserved_contents = mw.ustring.match(reserved_contents, "[^%,]+,(.*)$")
         until not reserved_contents
         until not reserved_contents
     end
     end
     local reserved_arraypart=_G
     local reserved_arraypart = _G
     while mw.ustring.match(reserved_function,"%.") do
     while mw.ustring.match(reserved_function, "%.") do
         reserved_functionpart,reserved_function=mw.ustring.match(reserved_function,"^(%a[^%.]*)%.(.*)$")
         reserved_functionpart, reserved_function = mw.ustring.match(reserved_function, "^(%a[^%.]*)%.(.*)$")
         reserved_arraypart=reserved_arraypart[reserved_functionpart]
         reserved_arraypart = reserved_arraypart[reserved_functionpart]
     end
     end
     local reserved_call=reserved_arraypart[reserved_function]
     local reserved_call = reserved_arraypart[reserved_function]
     if type(reserved_call)~="function" then
     if type(reserved_call) ~= "function" then
         return tostring(reserved_call)
         return tostring(reserved_call)
     else reserved_output={reserved_call(unpack(reserved_value))}
     else  
    reserved_output = {reserved_call(unpack(reserved_value))}
         return reserved_output[reserved_return or 1]
         return reserved_output[reserved_return or 1]
     end
     end
Line 144: Line 148:
--]]
--]]
function p.invoke(frame)
function p.invoke(frame)
local func
local pframe, usedpargs = frame:getParent(), {}
local offset
-- get module and function names from parent args if not provided
if frame.args[1] ~= nil then
local pfargs = setmetatable({frame.args[1], frame.args[2]}, {__index = table})
local m = require('Module:' .. frame.args[1])
if not pfargs[1] then
if frame.args[2] ~= nil then
pfargs[1], usedpargs[1] = pframe.args[1], true
func = m[frame.args[2]]
if not pfargs[2] then
frame.args = frame:getParent().args
pfargs[2], usedpargs[2] = pframe.args[2], true
offset = 0
end
else
elseif not pfargs[2] then
frame.args = frame:getParent().args
pfargs[2], usedpargs[1] = pframe.args[1], true
func = m[frame.args[1]]
end
offset = 1
-- repack sequential args
for i, v in ipairs(pframe.args) do
if not usedpargs[i] then
pfargs:insert(v)
usedpargs[i] = true
end
end
else
frame.args = frame:getParent().args
func = require('Module:' .. frame.args[1])[frame.args[2]]
offset = 2
end
end
local args = {}
-- copy other args
for _, v in ipairsAtOffset(frame.args, offset) do
for k, v in pairs(pframe.args) do
table.insert(args, v)
if not pfargs[k] and not usedpargs[k] then
pfargs[k], usedpargs[k] = v, true
end
end
end
frame.args = args
-- #invoke off parent frame so the new frame has the same parent
return func(frame)
return pframe:callParserFunction{name = '#invoke', args = pfargs}
end
end


return p
return p

Latest revision as of 09:25, 6 August 2024

Documentation for this module may be created at Module:LuaCall/doc

-- Imported from Wikipedia 
-- Licensed as CC BY-SA 4.0
-- See edit history for attribution
local p={}

function p.main(frame)
    local parent = frame.getParent(frame) or {}
    local reserved_value = {}
    local reserved_function, reserved_contents
    for k, v in pairs(parent.args or {}) do
        _G[k] = tonumber(v) or v -- transfer every parameter directly to the global variable table
    end
    for k, v in pairs(frame.args or {}) do
        _G[k] = tonumber(v) or v -- transfer every parameter directly to the global variable table
    end
     --- Alas Scribunto does NOT implement coroutines, according to
     --- http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#string.format
     --- this will not stop us from trying to implement one single lousy function call
    if _G[1] then
        reserved_function, reserved_contents = mw.ustring.match(_G[1], "^%s*(%a[^%s%(]*)%(([^%)]*)%)%s*$")
    end
    if reserved_contents then
        local reserved_counter = 0
        repeat
            reserved_counter = reserved_counter + 1
            reserved_value[reserved_counter] = _G[mw.ustring.match(reserved_contents, "([^%,]+)")]
            reserved_contents = mw.ustring.match(reserved_contents, "[^%,]+,(.*)$")
        until not reserved_contents
    end
    local reserved_arraypart = _G
    while mw.ustring.match(reserved_function, "%.") do
        reserved_functionpart, reserved_function = mw.ustring.match(reserved_function, "^(%a[^%.]*)%.(.*)$")
        reserved_arraypart = reserved_arraypart[reserved_functionpart]
    end
    local reserved_call = reserved_arraypart[reserved_function]
    if type(reserved_call) ~= "function" then
        return tostring(reserved_call)
    else 
    	reserved_output = {reserved_call(unpack(reserved_value))}
        return reserved_output[reserved_return or 1]
    end
end

local function tonumberOrString(v)
	return tonumber(v) or v:gsub("^\\", "", 1)
end

local function callWithTonumberOrStringOnPairs(f, ...)
	local args = {}
	for _, v in ... do
		table.insert(args, tonumberOrString(v))
	end
	return (f(unpack(args)))
end

--[[
------------------------------------------------------------------------------------
-- ipairsAtOffset

-- This is an iterator for arrays. It can be used like ipairs, but with
-- specified i as first index to iterate. i is an offset from 1
--
------------------------------------------------------------------------------------
--]]
local function ipairsAtOffset(t, i)
	local f, s, i0 = ipairs(t)
	return f, s, i0+i
end

local function get(s)
	local G = _G; for _ in mw.text.gsplit(
		mw.text.trim(s, '%s'), '%s*%.%s*'
	) do
		G = G[_]
	end
	return G
end

--[[
------------------------------------------------------------------------------------
-- call
--
-- This function is usually useful for debugging template parameters.
-- Prefix parameter with backslash (\) to force interpreting parameter as string.
-- The leading backslash will be removed before passed to Lua function.
--
-- Example:
--    {{#invoke:LuaCall|call|mw.log|a|1|2|3}} will return results of mw.log('a', 1, 2, 3)
--    {{#invoke:LuaCall|call|mw.logObject|\a|321|\321| \321 }} will return results of mw.logObject('a', 321, '321', ' \\321 ')
--
-- This example show the debugging to see which Unicode characters are allowed in template parameters,
--    {{#invoke:LuaCall|call|mw.ustring.codepoint|{{#invoke:LuaCall|call|mw.ustring.char|0x0061}}}} return 97
--    {{#invoke:LuaCall|call|mw.ustring.codepoint|{{#invoke:LuaCall|call|mw.ustring.char|0x0000}}}} return 65533
--    {{#invoke:LuaCall|call|mw.ustring.codepoint|{{#invoke:LuaCall|call|mw.ustring.char|0x0001}}}} return 65533
--    {{#invoke:LuaCall|call|string.format|0x%04x|{{#invoke:LuaCall|call|mw.ustring.codepoint|{{#invoke:LuaCall|call|mw.ustring.char|0x0002}}}}}} return 0xfffd
--    {{#invoke:LuaCall|call|string.format|0x%04x|{{#invoke:LuaCall|call|mw.ustring.codepoint|{{#invoke:LuaCall|call|mw.ustring.char|0x007e}}}}}} return 0x007e
--    {{#invoke:LuaCall|call|string.format|0x%04x|{{#invoke:LuaCall|call|mw.ustring.codepoint|{{#invoke:LuaCall|call|mw.ustring.char|0x007f}}}}}} return 0x007f
--    {{#invoke:LuaCall|call|string.format|0x%04x|{{#invoke:LuaCall|call|mw.ustring.codepoint|{{#invoke:LuaCall|call|mw.ustring.char|0x0080}}}}}} return 0x0080
--    {{#invoke:LuaCall|call|string.format|0x%04x|{{#invoke:LuaCall|call|mw.ustring.codepoint|{{#invoke:LuaCall|call|mw.ustring.char|0x00a0}}}}}} return 0x00a0
--
------------------------------------------------------------------------------------
--]]
function p.call(frame)
	return callWithTonumberOrStringOnPairs(get(frame.args[1]),
		ipairsAtOffset(frame.args, 1)
	)
end

--local TableTools = require('Module:TableTools')
--[[
------------------------------------------------------------------------------------
-- get
--
-- Example:
--    {{#invoke:LuaCall|get| math.pi }} will return value of math.pi
--    {{#invoke:LuaCall|get|math|pi}} will return value of math.pi
--    {{#invoke:LuaCall|get| math |pi}} will return value of _G[' math '].pi
--    {{#invoke:LuaCall|get|_G| math.pi }} will return value of _G[' math.pi ']
--    {{#invoke:LuaCall|get|obj.a.5.c}} will return value of obj.a['5'].c
--    {{#invoke:LuaCall|get|obj|a|5|c}} will return value of obj.a[5].c
--
------------------------------------------------------------------------------------
--]]
function p.get(frame)
	-- #frame.args always return 0, regardless of number of unnamed
	-- template parameters, so check manually instead
	if frame.args[2] == nil then
		-- not do tonumber() for this args style,
		-- always treat it as string,
		-- so 'obj.1' will mean obj['1'] rather obj[1]
		return get(frame.args[1])
	else
		local G = _G
		for _, v in ipairs(frame.args) do
			G = G[tonumberOrString(v)]
		end
		return G
	end
end

--[[
------------------------------------------------------------------------------------
-- invoke
--
-- This function is used by Template:Invoke
--
------------------------------------------------------------------------------------
--]]
function p.invoke(frame)
	local pframe, usedpargs = frame:getParent(), {}
	-- get module and function names from parent args if not provided
	local pfargs = setmetatable({frame.args[1], frame.args[2]}, {__index = table})
	if not pfargs[1] then
		pfargs[1], usedpargs[1] = pframe.args[1], true
		if not pfargs[2] then
			pfargs[2], usedpargs[2] = pframe.args[2], true
		end
	elseif not pfargs[2] then
		pfargs[2], usedpargs[1] = pframe.args[1], true
	end
	-- repack sequential args
	for i, v in ipairs(pframe.args) do
		if not usedpargs[i] then
			pfargs:insert(v)
			usedpargs[i] = true
		end
	end
	-- copy other args
	for k, v in pairs(pframe.args) do
		if not pfargs[k] and not usedpargs[k] then
			pfargs[k], usedpargs[k] = v, true
		end
	end
	-- #invoke off parent frame so the new frame has the same parent
	return pframe:callParserFunction{name = '#invoke', args = pfargs}
end

return p