local mps = {} mps.util = require("Module:ModulePageSystem/util") local dictionary = require("Module:Dictionary") mps.FUNC_HASVALUE_TRUE = function() return true end mps.FUNC_HASVALUE_FALSE = function() return false end mps.FUNC_VALUE_NIL = function() return nil end --定义一个常量,表示该节点值不存在。 mps.NOVALUE = { hasValue = mps.FUNC_HASVALUE_FALSE, value = mps.FUNC_VALUE_NIL } --[[ 验证一个节点是否是格式正确的对象。 通过检测数个字段、函数是否存在,类型是否正确来验证节点格式是否正确。 ----- node - 将要验证的节点。 validations - 指定了验证的类型,若有多个验证类型,则以空白字符分隔,验证结果将取与。 value - 该节点支持存储值。 sub - 该节点支持包含次级节点。 ----- 返回:多个验证类型结果的与值。 --]] function mps.validate(node, validations) if node == nil then return false elseif type(node) ~= "table" then return false end if type(validations) == "table" then validations = table.concat(validations, " ") elseif type(validations) ~= "string" then return false end for validation in mw.ustring.gmatch(validations, "%S+") do if validation == "value" then if type(node.hasValue) ~= "function" or type(node.value) ~= "function" then return false end elseif validation == "ref" then if type(node.getTarget) ~= "function" then return false end elseif validation == "sub" then if type(node.plain) ~= "function" or type(node.wiki) ~= "function" or type(node.ref) ~= "function" or type(node.sub) ~= "function" or type(node.substart) ~= "function" or type(node.subend) ~= "function" or type(node.format) ~= "function" or type(node.find) ~= "function" then return false end else mps.util.error(mw.ustring.format("不支持的验证类型:\"%s\"", validation), mps.util.ELID_WARNING) return false end end return true end --[[ 从指定的节点开始按路径查找值。 ----- node - 指定的节点。 path - 路径 ----- 返回:路径name处的值。 --]] function mps.find(node, path, params) if type(params) ~= "table" then params = {} end if type(params.path) ~= "table" then params.path = {} end if type(params.basestack) ~= "table" then params.basestack = {} end if type(params.refhistory) ~= "table" then params.refhistory = dictionary.create() end if node == nil or type(node) == "string" then -- node为表示以这个字符串为网页路径创建的节点。 -- 若node为空,则设为调用页面的标题。 node = node or mw.title.getCurrentTitle().prefixedText -- 尝试加载页面。 local root = mps.load({}, node) if mps.validate(node, "value") and value:hasValue(params) then -- 以这个字符串为基地址创建根节点。 node = mps.create(node) else node = root end end if params.root == nil then params.root = node end -- 将自身设为查询根节点。 local value local subpath = mps.util.pathSplit(path) -- 截取顶层目录为当前名称;其余为下级名称。 local name = subpath[1] table.remove(subpath, 1) table.insert(params.path, name) -- 将当前名称添加到路径中。 table.insert(params.basestack, { node.base }) -- 将当前所属页面路径添加到页面路径堆栈中。(由于所属页面路径可能为nil,故用table包裹。) -- 筛选子节点。子节点是否为底层节点将对其是否会被接受有影响。 -- 底层节点:接受值节点;不接受含有子节点的节点。 -- 非底层节点:接受含有子节点的节点;不接受值节点。 local findInternal = function(_value) -- 获取节点值。 if mps.validate(_value, "ref") then -- 如果是引用节点 -- 获取引用节点指向的节点。 _value = _value:getTarget(params) end if (params.novalidation or false) == true then -- 若忽略子节点是否为底层节点对其是否会被接受的影响。 if #subpath == 0 then -- 仅当子节点为底层节点时生效。 if (mps.validate(_value, "value") and _value:hasValue(params)) or mps.validate(_value, "sub") then -- 仅在子节点是含有值的值节点或者含有子节点的节点时,直接返回子节点本身。 return _value end end end if #subpath == 0 and mps.validate(_value, "value") then -- 如果是底层节点,且节点是值节点。 if _value:hasValue(params) then -- 如果有值则返回节点。 return _value end elseif #subpath ~= 0 and mps.validate(_value, "sub") then -- 如果不是底层节点,且节点含有子节点。 -- 进行递归查找。 _value = _value:find(subpath, params) if mps.validate(_value, "value") and _value:hasValue(params) then -- 子节点查找到值则返回节点。 return _value elseif mps.validate(_value, "sub") then -- 从findInternal返回的含有子节点的节点绝对是忽略层级影响。 return _value end elseif #subpath == 0 and not (mps.validate(_value, "value") or mps.validate(_value, "sub")) then -- 节点不是任何节点,且是底层节点。 mps.util.error("非节点对象。", mps.util.ELID_ERROR) end return mps.NOVALUE -- 查找不到子节点树的值。 end if mps.validate(node, "sub") then -- 本节点含有子节点。 -- 寻找键name。 if node.dic:hasKey(name) then value = findInternal(node.dic:getValue(name)) if mps.validate(value, "value") and value:hasValue(params) then table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。 table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。 return value elseif mps.validate(value, "sub") then -- 从findInternal返回的含有子节点的节点绝对是忽略层级影响。 table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。 table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。 return value end end -- 寻找通用函数formatfunc。 if node.formatfunc ~= nil then -- 找到通用函数formatfunc。 -- 获取处理结果。 value = node:formatfunc(name, params) value = findInternal(value) if mps.validate(value, "value") and value:hasValue(params) then table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。 table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。 return value elseif mps.validate(value, "sub") then -- 从findInternal返回的含有子节点的节点绝对是忽略层级影响。 table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。 table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。 return value end end end -- 尝试加载页面。 local fullpagename = nil -- 沿堆栈向下查询上级节点的基路径。 for i = #params.basestack, 1, -1 do if #params.basestack[i] ~= 0 then fullpagename = params.basestack[i][1] break; end end if fullpagename == nil then -- 无法获取基路径。 mps.util.error(mw.ustring.format("无法获取节点“%s”基路径,因为上层节点均未定义基路径。", mps.util.pathCombine(params.path)), mps.util.ELID_WARNING) else fullpagename = mw.ustring.format("%s/%s", fullpagename, table.concat(mps.util.pathSplit(params.path), "/")) -- 要加载的页面。 value = findInternal(mps.load(node, fullpagename)) if mps.validate(value, "value") and value:hasValue(params) then -- 页面加载成功。 table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。 table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。 return value elseif mps.validate(value, "sub") then -- 从findInternal返回的含有子节点的节点绝对是忽略层级影响。 table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。 table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。 return value end end table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。 table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。 return mps.NOVALUE -- 查询不到值。 end mps.FUNC_SUBEND_COMMON = function(self) mps.util.error("语法错误:subend。(当前并未处于定义子项的上下文。)", mps.util.ELID_WARNING) return self end --[[ 从一个页面地址创建一个节点。 ----- page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。 ----- 返回:新节点。 --]] function mps.create(page) if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("在create中参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) elseif page == nil then -- 检测页面地址是否为空。 mps.util.error(mw.ustring.format("页面地址为空。", page), mps.util.ELID_WARNING) elseif type(page) == "string" and mw.title.new(page) == nil then -- 检测页面地址是否包含不合法字符。 mps.util.error(mw.ustring.format("页面地址“%s”包含不合法字符。", page), mps.util.ELID_ERROR) end local prototype = { dic = dictionary.create(function(k1, k2) return mps.util.normalize(k1) == mps.util.normalize(k2) end), base = page, -- 本节点所在的页面地址。 inheritbase = "", -- [未启用]以定义了页面地址的最近一个上层节点开始到本节点的路径,即本节点的“继承”得到的基路径。 plain = mps.registerPlain, wiki = mps.registerWiki, ref = mps.registerRef, sub = mps.registerSubpage, substart = mps.registerNewSubpage, subend = mps.FUNC_SUBEND_COMMON, format = mps.registerFormat, find = mps.find } return prototype end mps.FUNC_VALUE_PLAIN = function(self) return self.innervalue end --[[ 创建一个纯文本的值节点。 ----- plain - 文本内容。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。 ----- 返回:新值节点。 --]] function mps.createPlain(plain, page) if plain ~= nil and type(plain) ~= "string" then mps.util.error(mw.ustring.format("参数plain的值类型错误。(应为%s,实际为%s)", "nil或string", type(plain)), mps.util.ELID_FATAL) end if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) elseif page == nil then -- 检测页面地址是否为空。 mps.util.error(mw.ustring.format("页面地址为空。", page), mps.util.ELID_WARNING) elseif type(page) == "string" and mw.title.new(page) == nil then -- 检测页面地址是否包含不合法字符。 mps.util.error(mw.ustring.format("页面地址“%s”包含不合法字符。", page), mps.util.ELID_ERROR) end return { base = page, innervalue = plain, hasValue = mps.FUNC_HASVALUE_TRUE, value = mps.FUNC_VALUE_PLAIN } end mps.FUNC_VALUE_WIKI = function(self, params) if self.innervalue == nil then return nil elseif params.plain then return self.innervalue elseif self.innerwiki ~= nil then return self.innerwiki elseif params == nil or params.frame == nil then mps.util.error(mw.ustring.format("无法获取wiki执行对象(frame的值为nil)。"), mps.util.ELID_WARNING) else self.innerwiki = params.frame:preprocess(self.innervalue) return self.innerwiki end end --[[ 创建一个Wiki内容的值节点。 ----- wiki - Wiki内容。 plain - 指示wiki参数的字符串值是否是纯文本,默认为true。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。 ----- 返回:新值节点。 --]] function mps.createWiki(wiki, plain, page) if wiki ~= nil and type(wiki) ~= "string" then mps.util.error(mw.ustring.format("参数wiki的值类型错误。(应为%s,实际为%s)", "nil或string", type(wiki)), mps.util.ELID_FATAL) end if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) elseif page == nil then -- 检测页面地址是否为空。 mps.util.error(mw.ustring.format("页面地址为空。", page), mps.util.ELID_WARNING) elseif type(page) == "string" and mw.title.new(page) == nil then -- 检测页面地址是否包含不合法字符。 mps.util.error(mw.ustring.format("页面地址“%s”包含不合法字符。", page), mps.util.ELID_ERROR) end local result = { base = page, innervalue = wiki, innerwiki = nil, hasValue = mps.FUNC_HASVALUE_TRUE, value = mps.FUNC_VALUE_WIKI } if (plain or false) ~= true then result.innerwiki = wiki end -- 默认是以纯文本形式创建。 return result end local find_ref = function(node, params) -- 查找引用历史,检测是否循环引用。 local flag = false local path = mps.util.pathCombine(params.path) if params.refhistory:hasKey(path) then -- 检测到循环引用。 flag = true else for _, rh_entry in params.refhistory:enum() do if rh_entry.value == node then -- 检测到循环引用。 flag = true break end end end if flag then local errorinfo = {} for _, _entry in params.refhistory:enum() do table.insert(errorinfo, _entry.key) end table.insert(errorinfo, rh_entry.key) mps.util.error("形成了循环引用。(“%s”)", table.concat(errorinfo, "”→“"), mps.util.ELID_ERROR) return mps.NOVALUE end -- 构建引用目标节点的路径。 local newPath if node.isabsolute then -- 绝对路径 newPath = mps.util.pathCombine(node.innervalue) else -- 相对路径 newPath = mps.util.pathCombine(params.path, mps.util.PARENTNODE, node.innervalue) end -- 构建查找引用目标节点的新参数。 local newParams = { frame = params.frame, plain = params.plain, refhistory = dictionary.create(nil, params.refhistory):add(path, node), -- 新建引用历史字典的副本,并将自身添加进去。 novalidation = true -- 忽略节点所处层级的影响。 } return mps.find(params.root, newPath, newParams), newParams end mps.FUNC_GETTARGET_REF = function(self, params) local target, newParams = find_ref(self, params) -- 递归获得指向的目标节点。 if mps.validate(target, "ref") then return target:getTarget(target, newParams) else return target end end --[[ 创建一个引用节点。 ----- target - 引用指向的目标。 isabsolute - 指示target参数表示的引用目标路径是否为绝对路径,默认为true。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。 ----- 返回:新引用节点。 --]] function mps.createRef(target, isabsolute, page) if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) elseif page == nil then -- 检测页面地址是否为空。 mps.util.error(mw.ustring.format("页面地址为空。", page), mps.util.ELID_WARNING) elseif type(page) == "string" and mw.title.new(page) == nil then -- 检测页面地址是否包含不合法字符。 mps.util.error(mw.ustring.format("页面地址“%s”包含不合法字符。", page), mps.util.ELID_ERROR) end return { base = page, innervalue = target, isabsolute = (isabsolute or false) == true, getTarget = mps.FUNC_GETTARGET_REF } end --[[ 向节点中注册一个纯文本的子值节点。 ----- parent - 节点本身。 name - 子节点名称。 plain - 文本内容。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。 ----- 返回:节点本身。(为语法糖提供支持) --]] function mps.registerPlain(parent, name, plain, page) if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end if type(name) ~= "string" then mps.util.error(mw.ustring.format("参数name的值类型错误。(应为%s,实际为%s)", "string", type(name)), mps.util.ELID_FATAL) end if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) end plain = mps.createPlain(plain, page or parent.page) if parent.dic:hasKey(name) then if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR) else parent.dic:setValue(name, plain) end else parent.dic:add(name, plain) end return parent end --[[ 向节点中注册一个Wiki内容的子值节点。 ----- parent - 节点本身。 name - 子节点名称。 wiki - Wiki内容。 plain - 指示wiki参数的字符串值是否是纯文本,默认为true。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。 ----- 返回:节点本身。(为语法糖提供支持) --]] function mps.registerWiki(parent, name, wiki, plain, page) if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end if type(name) ~= "string" then mps.util.error(mw.ustring.format("参数name的值类型错误。(应为%s,实际为%s)", "string", type(name)), mps.util.ELID_FATAL) end if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) end wiki = mps.createWiki(wiki, plain, page or parent.page) if parent.dic:hasKey(name) then if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR) else parent.dic:setValue(name, wiki) end else parent.dic:add(name, wiki) end return parent end --[[ 向节点中注册一个引用的子值节点。 ----- parent - 节点本身。 names - 所有引用子节点的名称(被指向目标的别名)。若类型为string则表示一个名称;若类型为table则表示多个名称。 target - 引用指向的目标。 isabsolute - 指示target参数表示的引用目标路径是否为绝对路径,默认为true。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。 ----- 返回:节点本身。(为语法糖提供支持) --]] function mps.registerRef(parent, names, target, isabsolute, page) if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end if type(names) == "string" then names = { names } elseif type(names) ~= "table" then mps.util.error(mw.ustring.format("参数names的值类型错误。(应为%s,实际为%s)", "string或table", type(names)), mps.util.ELID_FATAL) end if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) end local ref for i, name in ipairs(names) do if type(name) ~= "string" then mps.util.error(mw.ustring.format("参数names的第%d项的值类型错误。(应为%s,实际为%s)", i, "string", type(name)), mps.util.ELID_FATAL) else ref = ref or mps.createRef(target, isabsolute, page or parent.base) -- 只创建一次,整个循环使用同一且唯一的引用节点。 if parent.dic:hasKey(name) then if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR) else parent.dic:setValue(name, ref) end else parent.dic:add(name, ref) end end end return parent end --[[ 向节点中注册一个现有的子节点。 这个子节点可以是从另一个页面导入、在本地定义的——node节点、储存了纯数据的普通Lua表。 ----- parent - 节点本身。 name - 子节点名称。 node - 将作为被添加的子节点的内容。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。 ----- 返回:节点本身。(为语法糖提供支持) --]] function mps.registerSubpage(parent, name, node, page) if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end if type(name) ~= "string" then mps.util.error(mw.ustring.format("参数name的值类型错误。(应为%s,实际为%s)", "string", type(name)), mps.util.ELID_FATAL) end if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) end if type(node) == "string" then -- 是从指定路径的页面导入的内容。 -- node默认是绝对路径,若需要引用节点所处页面路径则需使用“%0”。 -- 获取绝对路径。 local path = mw.ustring.gsub(node, "%%[%%0]", function(m) if m == "%%" then return "%" elseif m == "%0" then return parent.base else return "" end end) -- 加载页面。 node = mps.load(parent, path) elseif type(node) == "table" then -- 是在本地定义的内容。 if mps.validate(node, "sub") then -- 检测是否是含有子节点的节点对象。 node = mps.derive(parent, node, page) -- 创建一个node的继承节点。 elseif mps.validate(node, "value") then -- 检测是否是值节点。 -- 设置所属页面路径。 node.base = node.base or page or parent.base -- 取值原则:本地定义的值节点的已定义所属页面路径 → 覆盖参数parent继承的页面路径 → 参数parent继承的页面路径。 else node = mps.parse(parent, node, page) -- 从一个普通Lua表转换成为节点。 end end if parent.dic:hasKey(name) then if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR) else parent.dic:setValue(name, node) end else parent.dic:add(name, node) end return parent end mps.FUNC_SUBEND_SUB = function(self) return self.parent end --[[ 向节点中注册一个子节点,并切换到子节点的上下文。 这个子节点可以是从另一个模板导入、在本地定义的——node节点、储存了纯数据的普通Lua表。 ----- parent - 节点本身。 name - 子节点名称。 node - 将作为被添加的子节点的内容。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。 ----- 返回:子节点本身。(为语法糖提供支持) --]] function mps.registerNewSubpage(parent, name) if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end if type(name) ~= "string" then mps.util.error(mw.ustring.format("参数name的值类型错误。(应为%s,实际为%s)", "string", type(name)), mps.util.ELID_FATAL) end local prototype = mps.create(parent.page) -- 获取一个标准的节点对象。 prototype.inheritbase = mps.util.pathCombine(parent.inheritbase, name) -- 继承本节点的路径组成新路径。 prototype.parent = parent -- 将本节点保存在parent字段中 prototype.subend = mps.FUNC_SUBEND_SUB -- 结束子节点定义,返回到其父节点,即本节点。 if parent.dic:hasKey(name) then if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR) else parent.dic:setValue(name, prototype) end else parent.dic:add(name, prototype) end return prototype end function mps.registerFormat(parent, format) if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end if format == nil then return elseif type(format) ~= "function" then mps.util.error(mw.ustring.format("通用函数formatfunc类型错误。(应为%s,实际为%s)", "function", type(format)), mps.util.ELID_FATAL) end if parent.formatfunc == nil then parent.formatfunc = format else mps.util.error("通用函数赋值失败,该节点已存在另外的通用函数。", mps.util.ELID_ERROR) end return parent end --[[ 从指定页面加载节点。 ----- parent - 本节点。 path - 包含节点的模块页面地址。 ----- 返回:指定页面包含的节点。 --]] function mps.load(parent, path) if type(parent) == "nil" then mps.util.error(mw.ustring.format("在load中参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end if type(path) ~= "string" then mps.util.error(mw.ustring.format("在load中参数path的值类型错误。(应为%s,实际为%s)", "string", type(path)), mps.util.ELID_FATAL) end local success, result = pcall(function(_path) local title = mw.title.new(_path) -- 获取要加载的页面的标题对象。 if title == nil then error(mw.ustring.format("页面“%s”路径格式不正确。"), _path) elseif title.exists then -- 加载的页面存在。 return require(_path) else mps.util.error(mw.ustring.format("页面“%s”不存在。", _path), mps.util.ELID_WARNING) return mps.NOVALUE end end, path) if result == mps.NOVALUE then return result elseif not success then mps.util.error(result, mps.util.ELID_ERROR) return mps.NOVALUE end local node = result if mps.validate(node, "sub") then -- 检测是否是节点对象。 node = mps.derive(parent, node, path) else node = mps.parse(parent, node, path) end return node end --[[ 将一个节点设为本节点的继承节点。 ----- parent - 本节点。 sub - 要被设为继承节点的节点。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。 ----- 返回:被设为继承节点的节点。 --]] function mps.derive(parent, sub, page) if type(parent) == "nil" then mps.util.error(mw.ustring.format("在derive中参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end if type(sub) ~= "table" then mps.util.error(mw.ustring.format("在derive中参数sub的值类型错误。(应为%s,实际为%s)", "table", type(sub)), mps.util.ELID_FATAL) end if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) end sub.base = sub.base or page or parent.base return sub end --[[ 将一个普通Lua表转换成为本节点的子节点。 ----- parent - 本节点。 tab - 要被转换为本节点的子节点的普通Lua表。 page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。 ----- 返回:转换后的节点。 --]] function mps.parse(parent, tab, page) if type(parent) == "nil" then mps.util.error(mw.ustring.format("在parse中参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end if type(tab) ~= "table" then mps.util.error(mw.ustring.format("在parse中参数tab的值类型错误。(应为%s,实际为%s)", "table", type(tab)), mps.util.ELID_FATAL) end if page ~= nil and type(page) ~= "string" then mps.util.error(mw.ustring.format("参数page的值类型错误。(应为%s,实际为%s)", "nil或string", type(page)), mps.util.ELID_FATAL) end local node = mps.create(page or parent.base) for _name, _node in pairs(tab) do if type(_node) == "table" then -- 递归转换Lua表中的节点 mps.registerSubpage(node, _name, _node) elseif type(_node) == "string" then -- 默认转换为纯文本节点。 mps.registerPlain(node, _name, _node) end end return node end return mps