Module:LuaCall

From Test Wiki
Revision as of 09:46, 8 March 2019 by w>Ans (fix bug, local function must be placed before import call)

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

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
        --if tonumber(v) then v=tonumber(v) end
        _G[k]=tonumber(v) or v -- transfer every parameter directly to the global variable table
        -- debuglog=debuglog..k.."="..v.."</nowiki><br /><nowiki>"
    end
    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
    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)
    elseif reserved_debug or not reserved_function then return mw.text.nowiki(debuglog)
    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 tonumberOrStringOnPairs(f, s, i)
	local args = {}
	for _, v in f, s, i do
		--table.insert(args, tonumber(v) or v:gsub("^\\", "", 1))
		table.insert(args, tonumberOrString(v))
	end
	return args
end

-- This function is modified from https://en.wikipedia.org/wiki/Module:Ustring?oldid=885619921
-- 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. 
function p.import(o)
	local p = {}

	for k, v in pairs(o) do if type(v)~='function' then p[k]=v else
		p[k] = function(frame)
			--local args = {}
			--for _, v in ipairs(frame.args) do
			--	--table.insert(args, tonumber(v) or v:gsub("^\\", "", 1))
			--	table.insert(args, tonumberOrString(v))
			--end
			--return (v(unpack(args)))
			return (v(unpack(
				tonumberOrStringOnPairs(ipairs(frame.args))
			)))
		end
	end
	end

	return p
end

-- ipairsAt()/ipairsAtOffset() will be proposed to be placed in [[Module:TableTools]]
--[[
------------------------------------------------------------------------------------
-- ipairsAt
--
-- This is an iterator for arrays. It can be used like ipairs, but with
-- specified i as first index to iterate.
--
--    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
--
------------------------------------------------------------------------------------
--]]
local function ipairsAtOffset(t, i)
	local f, s, i0 = ipairs(t)
	return f, s, i0+i
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 G = _G; for _ in mw.text.gsplit(
		mw.text.trim(s, '%s'), '%s*%.%s*'
	) do
		G = G[_]
	end
	return G
end

--[[
------------------------------------------------------------------------------------
-- call
--
-- 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}} will return results of mw.logObject('a', 321)
--
------------------------------------------------------------------------------------
--]]
function p.call(frame)
	--local f = mw.text.trim(frame.args[1], '%s')
	--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

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 use length() instead
	if TableTools.length(frame.args) == 1 then
		-- not do tonumber() for this args style,
		-- always treat it as string,
		-- 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])
	else
		--local args = {}
		local G = _G
		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)]
		end
		--local G = _G; for _, v in ipairs(args) do
		--	G = G[v]
		--end
		return G
	end
end

return p