Module:LuaCall: Difference between revisions
Jump to navigation
Jump to search
Content deleted Content added
use vararg for pairs() result |
Note: Since imported from Wikipedia, this is licensed as Creative Commons Attribution-ShareAlike License 4.0. |
||
(30 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 |
|||
-- 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 |
||
_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) |
|||
reserved_output = {reserved_call(unpack(reserved_value))} |
|||
return reserved_output[reserved_return or 1] |
return reserved_output[reserved_return or 1] |
||
end |
end |
||
Line 46: | Line 46: | ||
end |
end |
||
local function |
local function callWithTonumberOrStringOnPairs(f, ...) |
||
local args = {} |
local args = {} |
||
for _, v in ... do |
for _, v in ... do |
||
--table.insert(args, tonumber(v) or v:gsub("^\\", "", 1)) |
|||
table.insert(args, tonumberOrString(v)) |
table.insert(args, tonumberOrString(v)) |
||
end |
end |
||
return args |
return (f(unpack(args))) |
||
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 |
|||
-- |
|||
-- will iterate over the pairs (3,t[3]),(4,t[4]),..., up to the first |
|||
-- integer key absent from the table. |
|||
-- |
|||
-- Note: The following is an example to do range iteration from 3 to j |
|||
-- |
|||
-- for i,v in p.ipairsAt(t, 3) do if i>j then break end; body end |
|||
-- |
|||
------------------------------------------------------------------------------------ |
|||
--]] |
|||
local function ipairsAt(t, i) |
|||
--return ipairsAtOffset(t, i-1) |
|||
-- This may be less overhead than return ipairsAtOffset() above |
|||
local f, s, i0 = ipairs(t) |
|||
return f, s, i0+i-1 |
|||
end |
|||
--[[ |
|||
------------------------------------------------------------------------------------ |
|||
-- ipairsAtOffset |
|||
-- |
|||
-- Like ipairsAt(), except that it treat argument i as offset from 1 |
|||
-- |
-- |
||
------------------------------------------------------------------------------------ |
------------------------------------------------------------------------------------ |
||
Line 93: | Line 67: | ||
return f, s, i0+i |
return f, s, i0+i |
||
end |
end |
||
local function iIteratorsAtOffset(t, i) |
|||
local f, s, i0 = ipairs(t) |
|||
return function (s, i) |
|||
local i, v = f(s, i); return v |
|||
end, s, i0+i |
|||
end |
|||
-- like ipairs() except that it return only value, rather than key/value pair |
|||
-- on each iteration |
|||
local function iIterators(t) return iIteratorsAtOffset(t, 0) end |
|||
local function get(s) |
local function get(s) |
||
Line 119: | Line 82: | ||
-- |
-- |
||
-- This function is usually useful for debugging template parameters. |
-- 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: |
-- Example: |
||
-- {{#invoke:LuaCall|call|mw.log|a|1|2|3}} will return results of mw.log('a', 1, 2, 3) |
-- {{#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}} will return results of mw.logObject('a', 321) |
-- {{#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, |
-- This example show the debugging to see which Unicode characters are allowed in template parameters, |
||
Line 137: | Line 102: | ||
--]] |
--]] |
||
function p.call(frame) |
function p.call(frame) |
||
return callWithTonumberOrStringOnPairs(get(frame.args[1]), |
|||
ipairsAtOffset(frame.args, 1) |
|||
--local args = {} |
|||
) |
|||
--for _, v in ipairsAtOffset(frame.args, 1) do |
|||
-- --table.insert(args, tonumber(v) or v:gsub("^\\", "", 1)) |
|||
-- table.insert(args, tonumberOrString(v)) |
|||
--end |
|||
local args = tonumberOrStringOnPairs(ipairsAtOffset(frame.args, 1)) |
|||
--local G = _G |
|||
--for _ in mw.text.gsplit( |
|||
-- mw.text.trim(frame.args[1], '%s'), '%s*%.%s*' |
|||
--) do |
|||
-- G = G[_] |
|||
--end |
|||
return (get(frame.args[1])(unpack(args))) |
|||
end |
end |
||
Line 165: | Line 119: | ||
-- {{#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 |
||
-- {{#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 |
||
-- {{#invoke:LuaCall|get}} will return value of _G |
|||
-- |
-- |
||
------------------------------------------------------------------------------------ |
------------------------------------------------------------------------------------ |
||
Line 171: | Line 124: | ||
function p.get(frame) |
function p.get(frame) |
||
-- #frame.args always return 0, regardless of number of unnamed |
-- #frame.args always return 0, regardless of number of unnamed |
||
-- template parameters, so |
-- template parameters, so check manually instead |
||
if frame.args[2] == nil then |
|||
-- TableTools.length() could be more expensive as it iterate to the |
|||
-- full length of the array |
|||
if frame.args[1] ~= nil and frame.args[2] == nil then |
|||
-- not do tonumber() for this args style, |
-- not do tonumber() for this args style, |
||
-- always treat it as string, |
-- always treat it as string, |
||
-- so 'obj.1' will mean obj['1'] rather obj[1] |
-- so 'obj.1' will mean obj['1'] rather obj[1] |
||
--local G = _G; for _ in mw.text.gsplit( |
|||
-- mw.text.trim(frame.args[1], '%s'), '%s*%.%s*' |
|||
--) do |
|||
-- G = G[_] |
|||
--end |
|||
--return G |
|||
return get(frame.args[1]) |
return get(frame.args[1]) |
||
else |
else |
||
--local args = {} |
|||
local G = _G |
local G = _G |
||
for _, v in ipairs(frame.args) do |
for _, v in ipairs(frame.args) do |
||
--table.insert(args, tonumber(v) or v:gsub("^\\", "", 1)) |
|||
--G = G[tonumber(v) or v:gsub("^\\", "", 1)] |
|||
G = G[tonumberOrString(v)] |
G = G[tonumberOrString(v)] |
||
end |
end |
||
--local G = _G; for _, v in ipairs(args) do |
|||
-- G = G[v] |
|||
--end |
|||
return G |
return G |
||
end |
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 |
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