置顶公告:【置顶】关于临时开启评论区所有功能的公告(2022.10.22) | 【置顶】关于本站Widget恢复使用的公告
  • 你好~!欢迎来到萌娘百科镜像站!如需查看或编辑,请联系本站管理员注册账号。
  • 本镜像站和其他萌娘百科的镜像站无关,请注意分别。

Module:ModulePageSystem

贴贴♀百科,万娘皆可贴的百科全书!转载请标注来源页面的网页链接,并声明引自贴贴百科。内容不可商用。
跳到导航 跳到搜索
Template-info.svg 模块文档  [创建] [刷新]
  1. local mps = {}
  2. mps.util = require("Module:ModulePageSystem/util")
  3. local dictionary = require("Module:Dictionary")
  4. mps.FUNC_HASVALUE_TRUE = function() return true end
  5. mps.FUNC_HASVALUE_FALSE = function() return false end
  6. mps.FUNC_VALUE_NIL = function() return nil end
  7. --定义一个常量,表示该节点值不存在。
  8. mps.NOVALUE = {
  9. hasValue = mps.FUNC_HASVALUE_FALSE,
  10. value = mps.FUNC_VALUE_NIL
  11. }
  12. --[[
  13. 验证一个节点是否是格式正确的对象。
  14. 通过检测数个字段、函数是否存在,类型是否正确来验证节点格式是否正确。
  15. -----
  16. node - 将要验证的节点。
  17. validations - 指定了验证的类型,若有多个验证类型,则以空白字符分隔,验证结果将取与。
  18. value - 该节点支持存储值。
  19. sub - 该节点支持包含次级节点。
  20. -----
  21. 返回:多个验证类型结果的与值。
  22. --]]
  23. function mps.validate(node, validations)
  24. if node == nil then return false
  25. elseif type(node) ~= "table" then return false
  26. end
  27. if type(validations) == "table" then validations = table.concat(validations, " ")
  28. elseif type(validations) ~= "string" then return false
  29. end
  30. for validation in mw.ustring.gmatch(validations, "%S+") do
  31. if validation == "value" then
  32. if type(node.hasValue) ~= "function" or
  33. type(node.value) ~= "function" then return false end
  34. elseif validation == "ref" then
  35. if type(node.getTarget) ~= "function" then return false end
  36. elseif validation == "sub" then
  37. if type(node.plain) ~= "function" or
  38. type(node.wiki) ~= "function" or
  39. type(node.ref) ~= "function" or
  40. type(node.sub) ~= "function" or
  41. type(node.substart) ~= "function" or
  42. type(node.subend) ~= "function" or
  43. type(node.format) ~= "function" or
  44. type(node.find) ~= "function" then return false end
  45. else
  46. mps.util.error(mw.ustring.format("不支持的验证类型:\"%s\"", validation), mps.util.ELID_WARNING)
  47. return false
  48. end
  49. end
  50. return true
  51. end
  52. --[[
  53. 从指定的节点开始按路径查找值。
  54. -----
  55. node - 指定的节点。
  56. path - 路径
  57. -----
  58. 返回:路径name处的值。
  59. --]]
  60. function mps.find(node, path, params)
  61. if type(params) ~= "table" then params = {} end
  62. if type(params.path) ~= "table" then params.path = {} end
  63. if type(params.basestack) ~= "table" then params.basestack = {} end
  64. if type(params.refhistory) ~= "table" then params.refhistory = dictionary.create() end
  65. if node == nil or type(node) == "string" then -- node为表示以这个字符串为网页路径创建的节点。
  66. -- 若node为空,则设为调用页面的标题。
  67. node = node or mw.title.getCurrentTitle().prefixedText
  68. -- 尝试加载页面。
  69. local root = mps.load({}, node)
  70. if mps.validate(node, "value") and value:hasValue(params) then
  71. -- 以这个字符串为基地址创建根节点。
  72. node = mps.create(node)
  73. else
  74. node = root
  75. end
  76. end
  77. if params.root == nil then params.root = node end -- 将自身设为查询根节点。
  78. local value
  79. local subpath = mps.util.pathSplit(path)
  80. -- 截取顶层目录为当前名称;其余为下级名称。
  81. local name = subpath[1]
  82. table.remove(subpath, 1)
  83. table.insert(params.path, name) -- 将当前名称添加到路径中。
  84. table.insert(params.basestack, { node.base }) -- 将当前所属页面路径添加到页面路径堆栈中。(由于所属页面路径可能为nil,故用table包裹。)
  85. -- 筛选子节点。子节点是否为底层节点将对其是否会被接受有影响。
  86. -- 底层节点:接受值节点;不接受含有子节点的节点。
  87. -- 非底层节点:接受含有子节点的节点;不接受值节点。
  88. local findInternal = function(_value)
  89. -- 获取节点值。
  90. if mps.validate(_value, "ref") then -- 如果是引用节点
  91. -- 获取引用节点指向的节点。
  92. _value = _value:getTarget(params)
  93. end
  94. if (params.novalidation or false) == true then -- 若忽略子节点是否为底层节点对其是否会被接受的影响。
  95. if #subpath == 0 then -- 仅当子节点为底层节点时生效。
  96. if (mps.validate(_value, "value") and _value:hasValue(params)) or mps.validate(_value, "sub") then
  97. -- 仅在子节点是含有值的值节点或者含有子节点的节点时,直接返回子节点本身。
  98. return _value
  99. end
  100. end
  101. end
  102. if #subpath == 0 and mps.validate(_value, "value") then -- 如果是底层节点,且节点是值节点。
  103. if _value:hasValue(params) then -- 如果有值则返回节点。
  104. return _value
  105. end
  106. elseif #subpath ~= 0 and mps.validate(_value, "sub") then -- 如果不是底层节点,且节点含有子节点。
  107. -- 进行递归查找。
  108. _value = _value:find(subpath, params)
  109. if mps.validate(_value, "value") and _value:hasValue(params) then -- 子节点查找到值则返回节点。
  110. return _value
  111. elseif mps.validate(_value, "sub") then -- 从findInternal返回的含有子节点的节点绝对是忽略层级影响。
  112. return _value
  113. end
  114. elseif #subpath == 0 and not (mps.validate(_value, "value") or mps.validate(_value, "sub")) then -- 节点不是任何节点,且是底层节点。
  115. mps.util.error("非节点对象。", mps.util.ELID_ERROR)
  116. end
  117. return mps.NOVALUE -- 查找不到子节点树的值。
  118. end
  119. if mps.validate(node, "sub") then -- 本节点含有子节点。
  120. -- 寻找键name。
  121. if node.dic:hasKey(name) then
  122. value = findInternal(node.dic:getValue(name))
  123. if mps.validate(value, "value") and value:hasValue(params) then
  124. table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。
  125. table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。
  126. return value
  127. elseif mps.validate(value, "sub") then -- 从findInternal返回的含有子节点的节点绝对是忽略层级影响。
  128. table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。
  129. table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。
  130. return value
  131. end
  132. end
  133. -- 寻找通用函数formatfunc。
  134. if node.formatfunc ~= nil then -- 找到通用函数formatfunc。
  135. -- 获取处理结果。
  136. value = node:formatfunc(name, params)
  137. value = findInternal(value)
  138. if mps.validate(value, "value") and value:hasValue(params) then
  139. table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。
  140. table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。
  141. return value
  142. elseif mps.validate(value, "sub") then -- 从findInternal返回的含有子节点的节点绝对是忽略层级影响。
  143. table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。
  144. table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。
  145. return value
  146. end
  147. end
  148. end
  149. -- 尝试加载页面。
  150. local fullpagename = nil
  151. -- 沿堆栈向下查询上级节点的基路径。
  152. for i = #params.basestack, 1, -1 do
  153. if #params.basestack[i] ~= 0 then
  154. fullpagename = params.basestack[i][1]
  155. break;
  156. end
  157. end
  158. if fullpagename == nil then
  159. -- 无法获取基路径。
  160. mps.util.error(mw.ustring.format("无法获取节点“%s”基路径,因为上层节点均未定义基路径。", mps.util.pathCombine(params.path)), mps.util.ELID_WARNING)
  161. else
  162. fullpagename = mw.ustring.format("%s/%s", fullpagename, table.concat(mps.util.pathSplit(params.path), "/")) -- 要加载的页面。
  163. value = findInternal(mps.load(node, fullpagename))
  164. if mps.validate(value, "value") and value:hasValue(params) then -- 页面加载成功。
  165. table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。
  166. table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。
  167. return value
  168. elseif mps.validate(value, "sub") then -- 从findInternal返回的含有子节点的节点绝对是忽略层级影响。
  169. table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。
  170. table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。
  171. return value
  172. end
  173. end
  174. table.remove(params.path, #params.path) -- 从路径中删除最后一段名称。
  175. table.remove(params.basestack, #params.basestack) -- 从页面路径堆栈中删除最后一段所属页面路径。
  176. return mps.NOVALUE -- 查询不到值。
  177. end
  178. mps.FUNC_SUBEND_COMMON = function(self)
  179. mps.util.error("语法错误:subend。(当前并未处于定义子项的上下文。)", mps.util.ELID_WARNING)
  180. return self
  181. end
  182. --[[
  183. 从一个页面地址创建一个节点。
  184. -----
  185. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。
  186. -----
  187. 返回:新节点。
  188. --]]
  189. function mps.create(page)
  190. 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)
  191. elseif page == nil then -- 检测页面地址是否为空。
  192. mps.util.error(mw.ustring.format("页面地址为空。", page), mps.util.ELID_WARNING)
  193. elseif type(page) == "string" and mw.title.new(page) == nil then -- 检测页面地址是否包含不合法字符。
  194. mps.util.error(mw.ustring.format("页面地址“%s”包含不合法字符。", page), mps.util.ELID_ERROR)
  195. end
  196. local prototype = {
  197. dic = dictionary.create(function(k1, k2) return mps.util.normalize(k1) == mps.util.normalize(k2) end),
  198. base = page, -- 本节点所在的页面地址。
  199. inheritbase = "", -- [未启用]以定义了页面地址的最近一个上层节点开始到本节点的路径,即本节点的“继承”得到的基路径。
  200. plain = mps.registerPlain,
  201. wiki = mps.registerWiki,
  202. ref = mps.registerRef,
  203. sub = mps.registerSubpage,
  204. substart = mps.registerNewSubpage,
  205. subend = mps.FUNC_SUBEND_COMMON,
  206. format = mps.registerFormat,
  207. find = mps.find
  208. }
  209. return prototype
  210. end
  211. mps.FUNC_VALUE_PLAIN = function(self) return self.innervalue end
  212. --[[
  213. 创建一个纯文本的值节点。
  214. -----
  215. plain - 文本内容。
  216. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。
  217. -----
  218. 返回:新值节点。
  219. --]]
  220. function mps.createPlain(plain, page)
  221. 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
  222. 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)
  223. elseif page == nil then -- 检测页面地址是否为空。
  224. mps.util.error(mw.ustring.format("页面地址为空。", page), mps.util.ELID_WARNING)
  225. elseif type(page) == "string" and mw.title.new(page) == nil then -- 检测页面地址是否包含不合法字符。
  226. mps.util.error(mw.ustring.format("页面地址“%s”包含不合法字符。", page), mps.util.ELID_ERROR)
  227. end
  228. return {
  229. base = page,
  230. innervalue = plain,
  231. hasValue = mps.FUNC_HASVALUE_TRUE,
  232. value = mps.FUNC_VALUE_PLAIN
  233. }
  234. end
  235. mps.FUNC_VALUE_WIKI = function(self, params)
  236. if self.innervalue == nil then return nil
  237. elseif params.plain then return self.innervalue
  238. elseif self.innerwiki ~= nil then return self.innerwiki
  239. elseif params == nil or params.frame == nil then
  240. mps.util.error(mw.ustring.format("无法获取wiki执行对象(frame的值为nil)。"), mps.util.ELID_WARNING)
  241. else
  242. self.innerwiki = params.frame:preprocess(self.innervalue)
  243. return self.innerwiki
  244. end
  245. end
  246. --[[
  247. 创建一个Wiki内容的值节点。
  248. -----
  249. wiki - Wiki内容。
  250. plain - 指示wiki参数的字符串值是否是纯文本,默认为true。
  251. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。
  252. -----
  253. 返回:新值节点。
  254. --]]
  255. function mps.createWiki(wiki, plain, page)
  256. 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
  257. 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)
  258. elseif page == nil then -- 检测页面地址是否为空。
  259. mps.util.error(mw.ustring.format("页面地址为空。", page), mps.util.ELID_WARNING)
  260. elseif type(page) == "string" and mw.title.new(page) == nil then -- 检测页面地址是否包含不合法字符。
  261. mps.util.error(mw.ustring.format("页面地址“%s”包含不合法字符。", page), mps.util.ELID_ERROR)
  262. end
  263. local result = {
  264. base = page,
  265. innervalue = wiki,
  266. innerwiki = nil,
  267. hasValue = mps.FUNC_HASVALUE_TRUE,
  268. value = mps.FUNC_VALUE_WIKI
  269. }
  270. if (plain or false) ~= true then result.innerwiki = wiki end -- 默认是以纯文本形式创建。
  271. return result
  272. end
  273. local find_ref = function(node, params)
  274. -- 查找引用历史,检测是否循环引用。
  275. local flag = false
  276. local path = mps.util.pathCombine(params.path)
  277. if params.refhistory:hasKey(path) then
  278. -- 检测到循环引用。
  279. flag = true
  280. else
  281. for _, rh_entry in params.refhistory:enum() do
  282. if rh_entry.value == node then
  283. -- 检测到循环引用。
  284. flag = true
  285. break
  286. end
  287. end
  288. end
  289. if flag then
  290. local errorinfo = {}
  291. for _, _entry in params.refhistory:enum() do
  292. table.insert(errorinfo, _entry.key)
  293. end
  294. table.insert(errorinfo, rh_entry.key)
  295. mps.util.error("形成了循环引用。(“%s”)", table.concat(errorinfo, "”→“"), mps.util.ELID_ERROR)
  296. return mps.NOVALUE
  297. end
  298. -- 构建引用目标节点的路径。
  299. local newPath
  300. if node.isabsolute then -- 绝对路径
  301. newPath = mps.util.pathCombine(node.innervalue)
  302. else -- 相对路径
  303. newPath = mps.util.pathCombine(params.path, mps.util.PARENTNODE, node.innervalue)
  304. end
  305. -- 构建查找引用目标节点的新参数。
  306. local newParams = {
  307. frame = params.frame,
  308. plain = params.plain,
  309. refhistory = dictionary.create(nil, params.refhistory):add(path, node), -- 新建引用历史字典的副本,并将自身添加进去。
  310. novalidation = true -- 忽略节点所处层级的影响。
  311. }
  312. return mps.find(params.root, newPath, newParams), newParams
  313. end
  314. mps.FUNC_GETTARGET_REF = function(self, params)
  315. local target, newParams = find_ref(self, params)
  316. -- 递归获得指向的目标节点。
  317. if mps.validate(target, "ref") then
  318. return target:getTarget(target, newParams)
  319. else
  320. return target
  321. end
  322. end
  323. --[[
  324. 创建一个引用节点。
  325. -----
  326. target - 引用指向的目标。
  327. isabsolute - 指示target参数表示的引用目标路径是否为绝对路径,默认为true。
  328. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。
  329. -----
  330. 返回:新引用节点。
  331. --]]
  332. function mps.createRef(target, isabsolute, page)
  333. 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)
  334. elseif page == nil then -- 检测页面地址是否为空。
  335. mps.util.error(mw.ustring.format("页面地址为空。", page), mps.util.ELID_WARNING)
  336. elseif type(page) == "string" and mw.title.new(page) == nil then -- 检测页面地址是否包含不合法字符。
  337. mps.util.error(mw.ustring.format("页面地址“%s”包含不合法字符。", page), mps.util.ELID_ERROR)
  338. end
  339. return {
  340. base = page,
  341. innervalue = target,
  342. isabsolute = (isabsolute or false) == true,
  343. getTarget = mps.FUNC_GETTARGET_REF
  344. }
  345. end
  346. --[[
  347. 向节点中注册一个纯文本的子值节点。
  348. -----
  349. parent - 节点本身。
  350. name - 子节点名称。
  351. plain - 文本内容。
  352. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。
  353. -----
  354. 返回:节点本身。(为语法糖提供支持)
  355. --]]
  356. function mps.registerPlain(parent, name, plain, page)
  357. if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end
  358. if type(name) ~= "string" then mps.util.error(mw.ustring.format("参数name的值类型错误。(应为%s,实际为%s)", "string", type(name)), mps.util.ELID_FATAL) end
  359. 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
  360. plain = mps.createPlain(plain, page or parent.page)
  361. if parent.dic:hasKey(name) then
  362. if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then
  363. mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR)
  364. else
  365. parent.dic:setValue(name, plain)
  366. end
  367. else
  368. parent.dic:add(name, plain)
  369. end
  370. return parent
  371. end
  372. --[[
  373. 向节点中注册一个Wiki内容的子值节点。
  374. -----
  375. parent - 节点本身。
  376. name - 子节点名称。
  377. wiki - Wiki内容。
  378. plain - 指示wiki参数的字符串值是否是纯文本,默认为true。
  379. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。
  380. -----
  381. 返回:节点本身。(为语法糖提供支持)
  382. --]]
  383. function mps.registerWiki(parent, name, wiki, plain, page)
  384. if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end
  385. if type(name) ~= "string" then mps.util.error(mw.ustring.format("参数name的值类型错误。(应为%s,实际为%s)", "string", type(name)), mps.util.ELID_FATAL) end
  386. 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
  387. wiki = mps.createWiki(wiki, plain, page or parent.page)
  388. if parent.dic:hasKey(name) then
  389. if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then
  390. mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR)
  391. else
  392. parent.dic:setValue(name, wiki)
  393. end
  394. else
  395. parent.dic:add(name, wiki)
  396. end
  397. return parent
  398. end
  399. --[[
  400. 向节点中注册一个引用的子值节点。
  401. -----
  402. parent - 节点本身。
  403. names - 所有引用子节点的名称(被指向目标的别名)。若类型为string则表示一个名称;若类型为table则表示多个名称。
  404. target - 引用指向的目标。
  405. isabsolute - 指示target参数表示的引用目标路径是否为绝对路径,默认为true。
  406. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。
  407. -----
  408. 返回:节点本身。(为语法糖提供支持)
  409. --]]
  410. function mps.registerRef(parent, names, target, isabsolute, page)
  411. if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end
  412. if type(names) == "string" then names = { names }
  413. elseif type(names) ~= "table" then mps.util.error(mw.ustring.format("参数names的值类型错误。(应为%s,实际为%s)", "string或table", type(names)), mps.util.ELID_FATAL)
  414. end
  415. 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
  416. local ref
  417. for i, name in ipairs(names) do
  418. if type(name) ~= "string" then
  419. mps.util.error(mw.ustring.format("参数names的第%d项的值类型错误。(应为%s,实际为%s)", i, "string", type(name)), mps.util.ELID_FATAL)
  420. else
  421. ref = ref or mps.createRef(target, isabsolute, page or parent.base) -- 只创建一次,整个循环使用同一且唯一的引用节点。
  422. if parent.dic:hasKey(name) then
  423. if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then
  424. mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR)
  425. else
  426. parent.dic:setValue(name, ref)
  427. end
  428. else
  429. parent.dic:add(name, ref)
  430. end
  431. end
  432. end
  433. return parent
  434. end
  435. --[[
  436. 向节点中注册一个现有的子节点。
  437. 这个子节点可以是从另一个页面导入、在本地定义的——node节点、储存了纯数据的普通Lua表。
  438. -----
  439. parent - 节点本身。
  440. name - 子节点名称。
  441. node - 将作为被添加的子节点的内容。
  442. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。
  443. -----
  444. 返回:节点本身。(为语法糖提供支持)
  445. --]]
  446. function mps.registerSubpage(parent, name, node, page)
  447. if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end
  448. if type(name) ~= "string" then mps.util.error(mw.ustring.format("参数name的值类型错误。(应为%s,实际为%s)", "string", type(name)), mps.util.ELID_FATAL) end
  449. 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
  450. if type(node) == "string" then -- 是从指定路径的页面导入的内容。
  451. -- node默认是绝对路径,若需要引用节点所处页面路径则需使用“%0”。
  452. -- 获取绝对路径。
  453. local path = mw.ustring.gsub(node, "%%[%%0]", function(m)
  454. if m == "%%" then return "%"
  455. elseif m == "%0" then return parent.base
  456. else return ""
  457. end
  458. end)
  459. -- 加载页面。
  460. node = mps.load(parent, path)
  461. elseif type(node) == "table" then -- 是在本地定义的内容。
  462. if mps.validate(node, "sub") then -- 检测是否是含有子节点的节点对象。
  463. node = mps.derive(parent, node, page) -- 创建一个node的继承节点。
  464. elseif mps.validate(node, "value") then -- 检测是否是值节点。
  465. -- 设置所属页面路径。
  466. node.base = node.base or page or parent.base -- 取值原则:本地定义的值节点的已定义所属页面路径 → 覆盖参数parent继承的页面路径 → 参数parent继承的页面路径。
  467. else
  468. node = mps.parse(parent, node, page) -- 从一个普通Lua表转换成为节点。
  469. end
  470. end
  471. if parent.dic:hasKey(name) then
  472. if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then
  473. mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR)
  474. else
  475. parent.dic:setValue(name, node)
  476. end
  477. else
  478. parent.dic:add(name, node)
  479. end
  480. return parent
  481. end
  482. mps.FUNC_SUBEND_SUB = function(self) return self.parent end
  483. --[[
  484. 向节点中注册一个子节点,并切换到子节点的上下文。
  485. 这个子节点可以是从另一个模板导入、在本地定义的——node节点、储存了纯数据的普通Lua表。
  486. -----
  487. parent - 节点本身。
  488. name - 子节点名称。
  489. node - 将作为被添加的子节点的内容。
  490. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。
  491. -----
  492. 返回:子节点本身。(为语法糖提供支持)
  493. --]]
  494. function mps.registerNewSubpage(parent, name)
  495. if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end
  496. if type(name) ~= "string" then mps.util.error(mw.ustring.format("参数name的值类型错误。(应为%s,实际为%s)", "string", type(name)), mps.util.ELID_FATAL) end
  497. local prototype = mps.create(parent.page) -- 获取一个标准的节点对象。
  498. prototype.inheritbase = mps.util.pathCombine(parent.inheritbase, name) -- 继承本节点的路径组成新路径。
  499. prototype.parent = parent -- 将本节点保存在parent字段中
  500. prototype.subend = mps.FUNC_SUBEND_SUB -- 结束子节点定义,返回到其父节点,即本节点。
  501. if parent.dic:hasKey(name) then
  502. if parent.dic:getValue(name) ~= nil and parent.dic:getValue(name):hasValue() then
  503. mps.util.error(mw.ustring.format("在对节点\"%s\"赋值时失败,该节点已存在另外的值。", name), mps.util.ELID_ERROR)
  504. else
  505. parent.dic:setValue(name, prototype)
  506. end
  507. else
  508. parent.dic:add(name, prototype)
  509. end
  510. return prototype
  511. end
  512. function mps.registerFormat(parent, format)
  513. if type(parent) == "nil" then mps.util.error(mw.ustring.format("参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end
  514. if format == nil then return
  515. elseif type(format) ~= "function" then mps.util.error(mw.ustring.format("通用函数formatfunc类型错误。(应为%s,实际为%s)", "function", type(format)), mps.util.ELID_FATAL)
  516. end
  517. if parent.formatfunc == nil then
  518. parent.formatfunc = format
  519. else
  520. mps.util.error("通用函数赋值失败,该节点已存在另外的通用函数。", mps.util.ELID_ERROR)
  521. end
  522. return parent
  523. end
  524. --[[
  525. 从指定页面加载节点。
  526. -----
  527. parent - 本节点。
  528. path - 包含节点的模块页面地址。
  529. -----
  530. 返回:指定页面包含的节点。
  531. --]]
  532. function mps.load(parent, path)
  533. if type(parent) == "nil" then mps.util.error(mw.ustring.format("在load中参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end
  534. if type(path) ~= "string" then mps.util.error(mw.ustring.format("在load中参数path的值类型错误。(应为%s,实际为%s)", "string", type(path)), mps.util.ELID_FATAL) end
  535. local success, result = pcall(function(_path)
  536. local title = mw.title.new(_path) -- 获取要加载的页面的标题对象。
  537. if title == nil then
  538. error(mw.ustring.format("页面“%s”路径格式不正确。"), _path)
  539. elseif title.exists then -- 加载的页面存在。
  540. return require(_path)
  541. else
  542. mps.util.error(mw.ustring.format("页面“%s”不存在。", _path), mps.util.ELID_WARNING)
  543. return mps.NOVALUE
  544. end
  545. end, path)
  546. if result == mps.NOVALUE then return result
  547. elseif not success then
  548. mps.util.error(result, mps.util.ELID_ERROR)
  549. return mps.NOVALUE
  550. end
  551. local node = result
  552. if mps.validate(node, "sub") then -- 检测是否是节点对象。
  553. node = mps.derive(parent, node, path)
  554. else
  555. node = mps.parse(parent, node, path)
  556. end
  557. return node
  558. end
  559. --[[
  560. 将一个节点设为本节点的继承节点。
  561. -----
  562. parent - 本节点。
  563. sub - 要被设为继承节点的节点。
  564. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。
  565. -----
  566. 返回:被设为继承节点的节点。
  567. --]]
  568. function mps.derive(parent, sub, page)
  569. if type(parent) == "nil" then mps.util.error(mw.ustring.format("在derive中参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end
  570. if type(sub) ~= "table" then mps.util.error(mw.ustring.format("在derive中参数sub的值类型错误。(应为%s,实际为%s)", "table", type(sub)), mps.util.ELID_FATAL) end
  571. 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
  572. sub.base = sub.base or page or parent.base
  573. return sub
  574. end
  575. --[[
  576. 将一个普通Lua表转换成为本节点的子节点。
  577. -----
  578. parent - 本节点。
  579. tab - 要被转换为本节点的子节点的普通Lua表。
  580. page - 定义这个节点的页面,应当是你在书写这个代码时所处的模块名页面全名。若为非空值,则将覆盖参数parent的继承。
  581. -----
  582. 返回:转换后的节点。
  583. --]]
  584. function mps.parse(parent, tab, page)
  585. if type(parent) == "nil" then mps.util.error(mw.ustring.format("在parse中参数parent的值类型错误。(应为%s,实际为%s)", "table", type(parent)), mps.util.ELID_FATAL) end
  586. if type(tab) ~= "table" then mps.util.error(mw.ustring.format("在parse中参数tab的值类型错误。(应为%s,实际为%s)", "table", type(tab)), mps.util.ELID_FATAL) end
  587. 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
  588. local node = mps.create(page or parent.base)
  589. for _name, _node in pairs(tab) do
  590. if type(_node) == "table" then
  591. -- 递归转换Lua表中的节点
  592. mps.registerSubpage(node, _name, _node)
  593. elseif type(_node) == "string" then
  594. -- 默认转换为纯文本节点。
  595. mps.registerPlain(node, _name, _node)
  596. end
  597. end
  598. return node
  599. end
  600. return mps