2023年政策修订增补工作正在进行中,欢迎参与!
Module:Sandbox/サンムル/WikiTable
< Module:Sandbox | サンムル
- 设计参数:
- ✔已实现
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运算式。- 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