今天是2024年11月23日 第47周 星期六

代人,时大变了。

我们生活在大地上,但我们的梦想超越天空。

模块:Loop

来自Akarin
DrTompkins讨论 | 贡献2023年4月12日 (三) 12:17的版本 (导入1个版本)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航 跳到搜索
50px 模块文档[创建]
local module = {}

local getArgs = require('Module:Arguments').getArgs

local nowiki_blocks = { 'do', 'condition' }

function pack_inner_loop(loop)
    -- 给内层循环的do参数包裹<nowiki>标签
    loop = mw.ustring.gsub(loop, '{{%s-[Ll]oop%s-|', '{{#invoke:loop|loop|')
    loop = mw.ustring.gsub(loop, '{{%s-[Ww]hile%s-|', '{{#invoke:loop|while|')
    loop = mw.ustring.gsub(loop, '{{%s-[Dd]o[Ww]hile%s-|', '{{#invoke:loop|dowhile|')
    return mw.ustring.gsub(loop, '({{#invoke:loop.*)', function(a)
        local i = 1
        local stack = {}
        local block_begin = -1
        local block_end = -1
        while (i < mw.ustring.len(a)) do
        	for _, block in ipairs(nowiki_blocks) do
	            if block_begin == -1 and mw.ustring.find(a, '^|%s-'..block..'%s-=', i) then
	                _, block_begin = mw.ustring.find(a, '^|%s-'..block..'%s-=', i)
	                block_begin = block_begin + 1
	                i = block_begin
	                
	                break
	            end
	        end
            if block_begin ~= -1 then
                if mw.ustring.sub(a, i, i + 2) == '{{{' then
                    i = i + 3
                    table.insert(stack, 3)
                elseif mw.ustring.sub(a, i, i + 1) == '{{' then
                    i = i + 2
                    table.insert(stack, 2)
                elseif mw.ustring.sub(a, i, i + 2) == '}}}' then
                    if #stack ~= 0 and stack[#stack] == 3 then
                        i = i + 3
                        table.remove(stack)
                    elseif #stack ~= 0 and stack[#stack] == 2 then
                        i = i + 2
                        table.remove(stack)
                    else
                        block_end = i - 1
                        break
                    end
                elseif mw.ustring.sub(a, i, i + 1) == '}}' then
                    if #stack ~= 0 then
                        i = i + 2
                        table.remove(stack)
                    else
                        block_end = i - 1
                        break
                    end
                elseif mw.ustring.sub(a, i, i) == '|' and #stack == 0 then
                    block_end = i - 1
                    break
                else
                    i = i + 1
                end
            else
                i = i + 1
            end
        end
        if block_begin ~= -1 and block_end == -1 then block_end = i - 1 end
        if block_begin ~= -1 and block_end ~= -1 then
        -- mw.log('发现内层循环 <nowiki>'..mw.ustring.sub(a, block_begin, block_end)..'</nowiki>')
        return mw.ustring.sub(a, 1, block_begin - 1)..'<nowiki>'..
               mw.text.trim(mw.ustring.sub(a, block_begin, block_end))..'</nowiki>'..
               pack_inner_loop(mw.ustring.sub(a, block_end + 1, -1))
        else
            return a
        end
    end )
end

function _loop(args, frame)
    local name = ''
    local _min = 1
    local _max = tonumber(args['max'] or args[1] or 10)
    local addend = tonumber(args[2] or 1)
    local loop = args['do'] or ''
    loop = mw.text.unstripNoWiki(loop)
    loop = string.gsub(loop, '<!%-%-.-%-%->', '')

    loop = pack_inner_loop(loop)
    local trim = mw.text.split(loop, '\n')
    loop = ''
    for i, v in ipairs(trim) do
        loop = loop..mw.text.trim(v)
    end

    for k, v in pairs(args) do
        if(k ~= 'do' and k ~= 'max' and k ~= 1 and k ~= 2) then
            name = string.gsub(k, '([%%%(%)%.%+%-%*%?%[%]%^%$])', '%%%1')
            _min = tonumber(v)
            break
        end
    end
    if name == '' then error('必须设置自增/自减的变量名!') end

    loop = mw.text.decode(loop)
    -- mw.log('循环体= '..loop)

    local result = ''
    if type(args[2]) == 'nil' and _min > _max and addend > 0 then addend = -1 end
    for i=_min, _max, addend do
        frame:callParserFunction('#vardefine', name, i)
        -- mw.log('执行循环体('..name..'='..tostring(i)..') '..loop)
        result = result..frame:preprocess(loop)
    end
    -- mw.log('循环结果 '..result)
    return result
end

function module.loop(frame)
    if (frame:getParent() or frame):getTitle() == 'Template:Loop' and
        frame.args['do'] == nil then
        args = getArgs(frame)
    else
        args = frame.args
    end
    return _loop(args, frame:getParent() or frame)
end

-- 向后兼容
function module.main(frame)
    return module.loop(frame)
end

-- 别名
module["for"] = function(frame)
    return module.loop(frame)
end

local _while = function(args, frame, dowhile)
    --初步处理条件语句
    local condition = args['condition'] or ''
    condition = mw.text.unstripNoWiki(condition)
    condition = string.gsub(condition, '<!%-%-.-%-%->', '')
    
    --修整条件语句的格式
    local trim = mw.text.split(condition, '\n')
    condition = ''
    for i, v in ipairs(trim) do
        condition = condition..mw.text.trim(v)
    end
    
    condition = mw.text.decode(condition)
    -- mw.log('循环体= '..condition)
    
    --初步处理循环体
    local loop = args['do'] or ''
    loop = mw.text.unstripNoWiki(loop)
    loop = string.gsub(loop, '<!%-%-.-%-%->', '')
    
    --处理循环体中的次级循环语句
    loop = pack_inner_loop(loop)
    --修整循环体中语句的格式
    local trim = mw.text.split(loop, '\n')
    loop = ''
    for i, v in ipairs(trim) do
        loop = loop..mw.text.trim(v)
    end

    loop = mw.text.decode(loop)
    -- mw.log('循环体= '..loop)

    local result = ''
    if dowhile then
        -- mw.log('执行循环体('..name..'='..tostring(i)..') '..loop)
        repeat result = result..frame:preprocess(loop) until frame:preprocess(condition) == ''
    else
        -- mw.log('执行循环体('..name..'='..tostring(i)..') '..loop)
        while frame:preprocess(condition) ~= '' do result = result..frame:preprocess(loop) end
    end
    -- mw.log('循环结果 '..result)
    return result
end

module["while"] = function(frame)
    if (frame:getParent() or frame):getTitle() == 'Template:While' and
        frame.args['do'] == nil then
        args = getArgs(frame)
    else
        args = frame.args
    end
    return _while(args, frame:getParent() or frame, false)
end

module.dowhile = function(frame)
    if (frame:getParent() or frame):getTitle() == 'Template:While' and
        frame.args['do'] == nil then
        args = getArgs(frame)
    else
        args = frame.args
    end
    return _while(args, frame:getParent() or frame, true)
end

function module.fornumargs(frame)
	local args = frame.args
	local parent = frame:getParent()
	local prefix = mw.text.trim(args[1] or '')
	local numname = mw.text.trim(args[2] or '')
	local valname = mw.text.trim(args[3] or '')
	local cond = mw.text.decode(mw.text.unstripNoWiki(mw.text.trim(args.cond or '')))
	local text = mw.text.decode(mw.text.unstripNoWiki(mw.text.trim(args[4] or '')))
	if not parent or numname == '' or valname == '' or text == '' then return '' end

	local expr = '^' .. mw.ustring.gsub(prefix, '([-+*?%%])', '%%%1') .. '(%d+)$'
	local keys = {}
	local output = {}
	for key, _ in pairs(parent.args) do
		local num = mw.ustring.match(key, expr)
		if num then
			table.insert(keys, tonumber(num))
		end
	end
	table.sort(keys)
	for _, idx in ipairs(keys) do
		parent:callParserFunction('#vardefine', numname, idx)
		parent:callParserFunction('#vardefine', valname, parent.args[prefix .. idx])
		if cond == '' or parent:preprocess(cond) ~= '' then
			table.insert(output, parent:preprocess(text))
		end
	end
	return table.concat(output)
end

return module