arrayName
:读取指定的array对象填充表格内容,array对象可能是由{{Array}}创建,也可以是使用[[模块:Var-array]]创建。offsetR
:array对象arrayName
左上角的单元格在输出表格中的行偏移量。offsetC
:array对象arrayName
左上角的单元格在输出表格中的列偏移量。t_<property>
:设置表格的property
属性。r<row>_<property>
:设置表格第row
行的property
属性。r<row>t
:设置yes
或no
(默认),指示表格第row
行的所有单元格是否均为标题单元格。c<column>_<property>
:设置表格第column
列的property
属性。c<column>t
:设置yes
或no
(默认),指示表格第column
列的所有单元格是否均为标题单元格。r<row>c<column>
:设置表格第row
行第column
列的内容。r<row>c<column>_<property>
:设置表格第row
行第column
列的property
属性。r<row>c<column>t
:设置yes
或no
(默认),指示表格第row
行第column
列的单元格是否为标题单元格。n_<name>
:创建一个名为name
的匹配规则。可以使用wiki运算式。
m_<name>
:为名为name
的匹配规则编写映射规则。可以使用wiki运算式。返回值必须为以下的格式:
<row>
:对行的映射。<row>,<column>
:对单元格的映射。e_<name>
:使用名为name
的匹配规则设置所有符合条件的单元格的内容。可以使用wiki运算式。e_<name>_<property>
:使用名为name
的匹配规则设置所有符合条件的单元格的property
属性。可以使用wiki运算式。et_<name>
:设置yes
或no
(默认),指示符合名为name
的匹配规则的所有单元格是否均为标题单元格。可以使用wiki运算式。所有标注了“可以使用wiki运算式。”字样的参数,均可以在这个参数的值中书写wiki的计算式,通过%r
和%c
等获取当前匹配中的行和列等数据。
若wiki计算式包含有可能会在作为参数传入前解析的解析器函数、模板、页面引用、标签等wiki文本,请用<nowiki>
和</nowiki>
嵌套。
local module = {} local var_array = require("Module:Var-array") local luaq = require("Module:Luaq") local dictionary = require("Module:Dictionary") function module.create(frame, overrides) local args = overrides or frame.args or { } local argDic = luaq.asQuery(args) :select(function(pair) return pair.key end) :groupBy(function(argName) if type(argName) == "number" then return "raw cell content" elseif mw.ustring.match(argName, "^t_") then return "table property" elseif mw.ustring.match(argName, "^n_") then return "named pattern" else local sub, suffix = mw.ustring.match(argName, "^[rc][1-9]%d*(([t_]?).*)$") if #sub == 0 then return "line content" elseif sub == "t" then return "line istitle" elseif suffix == "_" then return "line property" end sub, suffix = mw.ustring.match(argName, "^r[1-9]%d*c[1-9]%d*(([t_]?).*)$") if #sub == 0 then return "cell content" elseif sub == "t" then return "cell istitle" elseif suffix == "_" then return "cell property" end end return "others" end) :where(function(group) return group.key ~= "others" end) :toDictionary( function(group) return group.key end, function(group) return group.query() end ) local keyComparer = function(k1, k2) if k1 == k2 then return true --引用相同 else return (math.max(0, k1.row or 0) == math.max(0, k2.row or 0)) and (math.max(0, k1.col or 0) == math.max(0, k2.col or 0)) end end local arrayName = args["arrayName"] local array = var_array.get(arrayName) --作为数据源的二维数组 local offsetR = args["offsetR"] or 0 local offsetC = args["offsetC"] or 0 local arrayCellContentDic = luaq.iasQuery(array or luaq.empty()) :selectMany( function(cells) return luaq.iasQuery(cells) end, function(cells, cell, rIndex, cIndex) return { position = { row = rIndex, col = cIndex }, content = cell } end ) :toDictionary( function(info) return info.position end, function(info) return info.content end, keyComparer ) local rawCellContentDic = (function() local defaultR, defaultC, arrayExists if array then defaultR, defaultC, arrayExists = 1, 1, true else defaultR, defaultC, arrayExists = offsetR, offsetC, false array = { } end if argDic:hasKey("raw cell content") then local dic = dictionary.create(keyComparer) local row, col = defaultR, defaultC local cells = { } local group = luaq.iasQuery(argDic.getValue("raw cell content")) :foreach(function(item) if (item == "|-") then row = row + 1 col = defaultC if not arrayExists then table.insert(array, cells) end cells = { } else dic:add({ row = row, col = col }, item) col = col + 1 if not arrayExists then table.insert(cells, item) end end end) if not arrayExists then table.insert(array, cells) end return dic else return dictionary.create(keyComparer) end end)() local calBoolean = function(text) return text ~= nil and mw.ustring.len(text) ~= 0 and text ~= "no" end local calProperties = function(prefix, func) func = func or function(s) return s end local globalPropertiesText = func(args[mw.string.format("%s_", prefix)] or "") local properties = dictionary.create() local iterator = mw.ustring.gmatch(globalPropertiesText, "%s*(%S+)=\"([^\"]*)\"") while true do local propertyName, propertyValue = iterator() if propertyName == nil then break else propertyValue = func(propertyValue) end properties:add(propertyName, mw.ustring.sub(propertyValue, 2, mw.ustring.len(propertyValue) - 1)) end luaq.asQuery(args) :select(function(pair) local subStr = mw.ustring.format("%s_", prefix) local length = mw.ustring.len(subStr) if mw.ustring.sub(pair.key, 1, length) == subStr and mw.ustring.len(pair.key) > length then return { propertyName = mw.ustring.sub(pair.key, length + 1), propertyValue = func(pair.value) } else return nil end end) :where(function(pair) return pair ~= nil end) :foreach(function(pair) if properties:hasKey(pair.propertyName) then properties:setValue(pair.propertyName, pair.propertyValue) else properties:add(pair.propertyName, pair.propertyValue) end end) return properties end local specificCellContentDic = (argDic:hasKey("cell content") and {luaq.iasQuery(argDic.getValue("cell content"))} or {luaq.empty})[1] :select(function(paramName) local row, col = mw.ustring.match(paramName, "^r([1-9]%d*)c([1-9]%d*)$") return { position = { row = tonumber(row), col = tonumber(col) }, content = args[paramName] } end) :toDictionary( function(info) return info.position end, function(info) return info.content end ) local specificCellIsTitleDic = (argDic:hasKey("cell istitle") and {luaq.iasQuery(argDic.getValue("cell istitle"))} or {luaq.empty})[1] :select(function(paramName) local row, col = mw.ustring.match(paramName, "^r([1-9]%d*)c([1-9]%d*)t$") return { position = { row = tonumber(row), col = tonumber(col) }, istitle = calBoolean(args[paramName]) } end) :toDictionary( function(info) return info.position end, function(info) return info.istitle end ) local specificCellPropertiesDic = (argDic:hasKey("cell property") and {luaq.iasQuery(argDic.getValue("cell property"))} or {luaq.empty})[1] :select(function(paramName) local row, col = mw.ustring.match(paramName, "^r([1-9]%d*)c([1-9]%d*)_") return { position = { row = tonumber(row), col = tonumber(col) }, properties = calProperties(mw.ustring.format("r%sc%s", row, col)) } end) :toDictionary( function(info) return info.position end, function(info) return info.properties end ) local specificLineContentTable = (argDic:hasKey("line content") and {luaq.iasQuery(argDic.getValue("line content"))} or {luaq.empty})[1] :select(function(paramName) local lineType, lineIndex = mw.ustring.match(paramName, "^([rc])([1-9]%d*)$") return { type = lineType, index = tonumber(lineIndex), content = args[paramName] } end) :query() local specificLineIsTitleTable = (argDic:hasKey("line istitle") and {luaq.iasQuery(argDic.getValue("line istitle"))} or {luaq.empty})[1] :select(function(paramName) local lineType, lineIndex = mw.ustring.match(paramName, "^([rc])([1-9]%d*)t$") return { type = lineType, index = tonumber(lineIndex), istitle = calBoolean(args[paramName]) } end) :query() local specificLinePropertiesTable = (argDic:hasKey("line property") and {luaq.iasQuery(argDic.getValue("line property"))} or {luaq.empty})[1] :select(function(paramName) local lineType, lineIndex = mw.ustring.match(paramName, "^([rc])([1-9]%d*)_") return { type = lineType, index = tonumber(lineIndex), properties = calProperties(mw.ustring.format("%s%s", lineType, lineIndex)) } end) local tablePropertiesDic = (argDic:hasKey("table property") and {calProperties("t")} or {dictionary.create()})[1] local patternDicsTable = (argDic:hasKey("named pattern") and {luaq.iasQuery(argDic.getValue("named pattern"))} or {luaq.empty})[1] :select(function(paramName) local patternName = mw.ustring.match(paramName, "^n_(.*)$") local pattern = args[paramName] or "" local mapping = args[mw.ustring.format("m_%s", patternName)] or "%r,%c" local gsubFunc = function(expression) local wikiText mw.ustring.gsub(expression, "%%[rcexy]", function(capture) if capture == "%r" then return tostring(info.position.row) elseif capture == "%c" then return tostring(info.position.col) elseif capture == "%e" then return mw.text.decode(mw.text.unstripNoWiki(info.content)) elseif capture == "%x" then return tostring(offsetC) elseif capture == "%y" then return tostring(offsetR) end end) return frame:preprocess(wikiText) end return luaq.iasQuery(array) :selectMany(function(cells, rIndex) return luaq.iasQuery(cells) :select(function(cell, cIndex) return { position = { row = rIndex, col = cIndex }, content = cell } end) end) :where(function(info) local patternResult = gsubFunc(pattern) return calBoolean(patternResult) end) :select(function(info) local mappingResult = gsubFunc(mapping) local row, col = mw.ustring.match(mappingResult, "^(%d*),?(%d*)$") if #row == 0 then row = 0 else row = tonumber(row) end if #col == 0 then col = 0 else col = tonumber(col) end return { mappedPosition = { row = row, col = col }, originalPosition = info.position, content = info.content } end) :selectMany( function(info) local t = { } local content = args[mw.ustring.format("c_%s", patternName)] if content then table.insert(t, { "pattern cell content", mw.text.decode(mw.text.unstripNoWiki(content)) }) end local istitle = args[mw.ustring.format("ct_%s", patternName)] if istitle then local istitleResult = frame:preprocess(mw.text.decode(mw.text.unstripNoWiki(istitle))) table.insert(t, { "pattern cell istitle", calBoolean(istitleResult) }) end local properties = calProperties( mw.ustring.format("c_%s", patternName), gsubFunc ) table.insert(t, { "pattern cell property", properties }) return luaq.iasQuery(t) end, function(info, rslt) return { position = info.mappedPosition, resultType = rslt[1], resultValue = rslt[2] } end ) :groupBy( function(info) return info.resultType end, function(info) return { position = info.pos, resultValue = info.resultValue } end ) :toDictionary( function(group) return group.key end, function(group) return group end ) end) :query() local propertiesToString = function(properties) return table.concat( luaq.select(properties, function(pair) return mw.ustring.format("%s=\"%s\"", pair.key, pair.value) end) :query(), " " ) end --输出逻辑 local tOutput = { } table.insert(tOutput, mw.ustring.format("{| %s", propertiesToString( luaq.aggregate( luaq.iasQuery(patternDicsTable) :select(function(dic) if dic:hasKey("pattern cell property") then return dic:getValue("pattern cell property") else return nil end end) :where(function(dic) return dic ~= nil end), function(ps1, ps2) return luaq.union(ps1, ps2, function(p1, p2) return keyComparer(p1.key, p2.key) end) end, dictionary.create(nil, tablePropertiesDic) ) ))) local maxRow = luaq.asQueryFrom( luaq.select(rawCellContentDic, function(pair) return pair.key.row or 0 end) :defaultIfEmpty(0) :max(), luaq.select(arrayCellContentDic, function(pair) return pair.key.row or 0 end) :defaultIfEmpty(0) :max(), luaq.iasQuery(patternDicsTable) :select(function(dic) return luaq.asQueryFrom( (dic:hasKey("pattern cell content") and {dic:getValue("pattern cell content")} or luaq.empty) :select(function(info) return info.position.row or 0 end) :defaultIfEmpty(0) :max(), (dic:hasKey("pattern cell istitle") and {dic:getValue("pattern cell istitle")} or luaq.empty) :select(function(info) return info.position.row or 0 end) :defaultIfEmpty(0) :max(), (dic:hasKey("pattern cell property") and {dic:getValue("pattern cell property")} or luaq.empty) :select(function(info) return info.position.row or 0 end) :defaultIfEmpty(0) :max() ).max() end) :defaultIfEmpty(0) :max(), luaq.select(arrayCellContentDic, function(pair) return pair.key.row or 0 end) :defaultIfEmpty(0) :max() ).max() for row = 1, maxRow do table.add(tOutput, "|-") local maxCol = luaq.asQueryFrom( luaq.select(colCellDic, function(pair) return pair.key.col or 0 end) :defaultIfEmpty(0) :max(), luaq.select(arrayCellContentDic, function(pair) return pair.key.col or 0 end) :defaultIfEmpty(0) :max(), luaq.iasQuery(patternDicsTable) :select(function(dic) return luaq.asQueryFrom( (dic:hasKey("pattern cell content") and {dic:getValue("pattern cell content")} or luaq.empty) :select(function(info) return info.position.col or 0 end) :defaultIfEmpty(0) :max(), (dic:hasKey("pattern cell istitle") and {dic:getValue("pattern cell istitle")} or luaq.empty) :select(function(info) return info.position.col or 0 end) :defaultIfEmpty(0) :max(), (dic:hasKey("pattern cell property") and {dic:getValue("pattern cell property")} or luaq.empty) :select(function(info) return info.position.col or 0 end) :defaultIfEmpty(0) :max() ).max() end) :defaultIfEmpty(0) :max(), luaq.select(arrayCellContentDic, function(pair) return pair.key.col or 0 end) :defaultIfEmpty(0) :max() ).max() for col = 1, maxCol do local istitle = (luaq.asQueryFrom( luaq.asQuery(specificCellIsTitleDic) :where(function(pair) return pair.key.row == row and pair.key.col == col end) :select(function(pair) return pair.value end) :first(nil), luaq.iasQuery(specificLineIsTitleTable) :where(function(info) if info.type == "r" then return info.index == row elseif info.type == "c" then return info.index == col end end) :select(function(info) return info.istitle end) :first(nil) ) :concat(luaq.iasQuery(patternDicsTable) :where(function(dic) return dic:hasKey("pattern cell istitle") end) :selectMany(function(dic) return dic:getValue("pattern cell istitle") end) :where(function(info) return (info.position.row == 0 or info.position.row == row) or (info.position.col == 0 or info.position.col == col) end) :select(function(info) return info.resultValue end) ) :where(function(it) return it ~= nil end) :first(false) and {"!"} or {"|"})[1] local properties = luaq.asQuery(specificCellPropertiesDic) :where(function(pair) return pair.key.row == row and pair.key.col == col end) :select(function(pair) return pair.value end) :concat(luaq.iasQuery(specificLinePropertiesTable) :where(function(info) if info.type == "r" then return info.index == row elseif info.type == "c" then return info.index == col end end) :select(function(info) return info.content end) ) :concat(luaq.iasQuery(patternDicsTable) :where(function(dic) return dic:hasKey("pattern cell property") end) :selectMany(function(dic) return dic:getValue("pattern cell property") end) :where(function(info) return (info.position.row == 0 or info.position.row == row) or (info.position.col == 0 or info.position.col == col) end) :select(function(info) return info.resultValue end) ) :aggregate( function(ps1, ps2) return luaq.union(ps1, ps2, function(p1, p2) return keyComparer(p1.key, p2.key) end) end, dictionary.create(keyComparer) ) local content = luaq.asQueryFrom( luaq.asQuery(specificCellContentDic) :where(function(pair) return pair.key.row == row and pair.key.col == col end) :select(function(pair) return pair.value end) :first(nil), luaq.iasQuery(specificLineContentTable) :where(function(info) if info.type == "r" then return info.index == row elseif info.type == "c" then return info.index == col end end) :select(function(info) return info.content end) :first(nil) ) :concat(luaq.iasQuery(patternDicsTable) :where(function(dic) return dic:hasKey("pattern cell content") end) :selectMany(function(dic) return dic:getValue("pattern cell content") end) :where(function(info) return (info.position.row == 0 or info.position.row == row) or (info.position.col == 0 or info.position.col == col) end) :select(function(info) return info.resultValue end) ) :concat(luaq.asQueryFrom( luaq.asQuery(arrayCellContentDic) :where(function(pair) return pair.key.row == row and pair.key.col == col end) :select(function(pair) return pair.value end) :first(nil), luaq.asQuery(rawCellContentDic) :where(function(pair) return pair.key.row == row and pair.key.col == col end) :select(function(pair) return pair.value end) :first(nil) )) :where(function(it) return it ~= nil end) :first("") if luaq.any(properties) then table.insert(mw.ustring.format("%s %s | %s", istitle, propertiesToString(properties), frame:preprocess(content))) else table.insert(mw.ustring.format("%s %s", istitle, frame:preprocess(content))) end --输出单元格 -- [raw cell content] => [array] => [named pattern cell content](<first> => <last>) =>[specific line content] =>[specific cell content] -- [named pattern cell istitle/properties](<first> => <last>) => [specific line istitle/properties] => [specific cell istitle/properties] end end table.insert("|}") return table.concat(tOutput, "\r\n") end return module