Module:LuaCall: Difference between revisions

From Test Wiki
Jump to navigation Jump to search
Content deleted Content added
p.call() refactor
Note: Since imported from Wikipedia, this is licensed as Creative Commons Attribution-ShareAlike License 4.0.
 
(49 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
--if tonumber(v) then v=tonumber(v) end
_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
-- debuglog=debuglog..k.."="..v.."</nowiki><br /><nowiki>"
end
end
for k,v in pairs(frame.args or {}) do
for k, v in pairs(frame.args or {}) do
--if tonumber(v) then v=tonumber(v) end
_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 18: 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
elseif reserved_debug or not reserved_function then return mw.text.nowiki(debuglog)
else reserved_output={reserved_call(unpack(reserved_value))}
reserved_output = {reserved_call(unpack(reserved_value))}
return reserved_output[reserved_return or 1]
return reserved_output[reserved_return or 1]
end
end
end
end


local function tonumberOrString(v)
-- This function is modified from https://en.wikipedia.org/wiki/Module:Ustring?oldid=885619921
return tonumber(v) or v:gsub("^\\", "", 1)
-- All frame's arguments are coerced as number type if possible. If you wish for something to remain a string, you can simply escape it by insert \ at the beginning of the string.
end
function p.import(o)
local p = {}


local function callWithTonumberOrStringOnPairs(f, ...)
for k, v in pairs(o) do
local args = {}
p[k] = function(frame)
for _, v in ... do
local args = {}
table.insert(args, tonumberOrString(v))
for _, v in ipairs(frame.args) do
table.insert(args, tonumber(v) or v:gsub("^\\", "", 1))
end
return (v(unpack(args)))
end
end
end
return (f(unpack(args)))

return p
end
end


-- ipairsAt()/ipairsAtOffset() will be proposed to be placed in [[Module:TableTools]]
--[[
--[[
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- ipairsAtOffset
-- ipairsAt

--
-- This is an iterator for arrays. It can be used like ipairs, but with
-- This is an iterator for arrays. It can be used like ipairs, but with
-- specified i as first index to iterate.
-- specified i as first index to iterate. i is an offset from 1
--
--
------------------------------------------------------------------------------------
-- for i,v in p.ipairsAt(t, 3) do body end
--]]
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.
-- will iterate over the pairs (3,t[3]),(4,t[4]),..., up to the first
-- Prefix parameter with backslash (\) to force interpreting parameter as string.
-- integer key absent from the table.
-- The leading backslash will be removed before passed to Lua function.
--
--
-- Example:
-- Note: The following is an example to do range iteration from 3 to j
-- {{#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,
-- for i,v in p.ipairsAt(t, 3) do if i>j then break end; body end
-- {{#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
--
--
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
--]]
--]]
local function ipairsAt(t, i)
function p.call(frame)
return callWithTonumberOrStringOnPairs(get(frame.args[1]),
--return ipairsAtOffset(t, i-1)
-- This may be less overhead than return ipairsAtOffset() above
ipairsAtOffset(frame.args, 1)
)
local f, s, i0 = ipairs(t)
return f, s, i0+i-1
end
end


--local TableTools = require('Module:TableTools')
--[[
--[[
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- get
-- ipairsAtOffset
--
--
-- Example:
-- Like ipairsAt(), except that it treat argument i as offset from 1
-- {{#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
--
--
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
--]]
--]]
local function ipairsAtOffset(t, i)
function p.get(frame)
-- #frame.args always return 0, regardless of number of unnamed
local f, s, i0 = ipairs(t)
-- template parameters, so check manually instead
return f, s, i0+i
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
end


--[[
--[[
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- call
-- invoke
--
--
-- This function is used by Template:Invoke
-- Example:
-- {{#invoke:LuaCall|call|mw.log|a|1|2|3}} will return mw.log('a', 1, 2, 3)
-- {{#invoke:LuaCall|call|mw.logObject|a|321}} will return mw.logObject('a', 321)
--
--
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
--]]
--]]
function p.call(frame)
function p.invoke(frame)
local args = {}
local pframe, usedpargs = frame:getParent(), {}
-- get module and function names from parent args if not provided
--local f = mw.text.trim(frame.args[1], '%s')
for _, v in ipairsAtOffset(frame.args, 1) do
local pfargs = setmetatable({frame.args[1], frame.args[2]}, {__index = table})
if not pfargs[1] then
table.insert(args, tonumber(v) or v:gsub("^\\", "", 1))
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
end
-- repack sequential args
local G = _G
for _, v in mw.text.split(
for i, v in ipairs(pframe.args) do
if not usedpargs[i] then
mw.text.trim(frame.args[1], '%s'), '%s*%.%s*'
pfargs:insert(v)
) do
G = G[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
end
-- #invoke off parent frame so the new frame has the same parent
return (G(unpack(args)))
return pframe:callParserFunction{name = '#invoke', args = pfargs}
end
end



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