Mô đun:GHS phrases

--------------------------------------------------------------------------------
-- Module:GHS phrases
--
-- main: reads GHS parameters (arguments like "H301", "P401")
-- and returns for each (listtype='abbr'):
-- phraseID visible; formal phrase text as
-- setID = "H" or "P"
-- phraseID = e.g. "H201", "P231+P234"
-- phrase text read from array tables in Module:GHS phrases/data
--
-- Implements: Template:GHS phrases
-- Helppage: Template:GHS phrases
-- Error category: , (mainspace pages only)
--
-- Also:
-- listAll(), numberOfPhrases(), listOmitRules(),
-- listtype, omit
--------------------------------------------------------------------------------
require('strict')
local r = {} -- "r" for return, so no confusion with setID P
local GHSdata = mw.loadData('Module:GHS phrases/data')
local getArgs = require('Module:Arguments').getArgs
local tTools = require('Module:TableTools')
local yesno = require('Module:Yesno')
local tArgName = {} -- named parameters (setid, omit, listtype)
local tMessagesToShow = {} -- the tail: Preview, Categories

--------------------------------------------------------------------------------
-- wlHelpPage
--
-- Formats page as Label
-- by default, sLabel == sSection
--------------------------------------------------------------------------------
local function wlHelpPage(sSection, sLabel)
local sHelpPage = 'Template:GHS phrases'

if sLabel == nil then sLabel = sSection end

if (sLabel or ) == then
sLabel =
else
sLabel = '|' .. sLabel
end
if (sSection or ) == then
sSection =
else
sSection = '' .. sSection
end
return .. sHelpPage .. sSection .. sLabel ..
end

--------------------------------------------------------------------------------
-- addErrorCategory
--
-- Formats as
-- or when in other namespace.
-- sCatsort option using: H, P, _
--------------------------------------------------------------------------------
local function addErrorCategory(sCatsort)
local pagetype = require('Module:Pagetype').main

local wlErrCat =
if pagetype() 'article' then -- mainspace only
if sCatsort nil then sCatsort = tArgName['setID'] end

if sCatsort == then
wlErrCat =
else
wlErrCat =
end
else
return
end

table.insert(tMessagesToShow, wlErrCat)
return
end

--------------------------------------------------------------------------------
-- addWarningCategory
--
-- Formats as
-- mainspace only, or when in other namespace.
-- sCatsort option using: H, P, U, ?, D, O
--------------------------------------------------------------------------------
local function addWarningCategory(sCatsort)
local pagetype = require('Module:Pagetype').main
if sCatsort == nil then sCatsort = tArgName['setID'] end

local wlWarnCat =
if pagetype() 'article' then -- mainspace only
if sCatsort then
wlWarnCat =
else
wlWarnCat =
end
else
return
end

table.insert(tMessagesToShow, wlWarnCat)
return
end

--------------------------------------------------------------------------------
-- addPreviewMsg
--------------------------------------------------------------------------------
local function addPreviewMsg(sMsg)
local previewWarn = require('Module:If preview')._warning
table.insert(tMessagesToShow, previewWarn({sMsg}))
return
end

--------------------------------------------------------------------------------
-- showPreviewMsg
--
-- show table tMessagesToShow
-- preview-messages and errorcat
-- all namespaces
--------------------------------------------------------------------------------
local function showPreviewMsg()
if tTools.size(tMessagesToShow) > 0 then
return table.concat(tMessagesToShow, )
else
return
end
end

--------------------------------------------------------------------------------
-- applyRemoveDuplicates
--
-- returns edited table, with double Codes removed
-- adds warning with codes.
-- base table tArgs is walked through by a iwalker that reads a singel code,
-- then a ikiller checks the upward part of the same table to delete all copies
-- ikiller starts at end of table, walks towards iwalker; then tArgs is compressed
-- iwalker steps 1 up in the freshly compressed table
-- Used: iArgs is sorted, and order stays same. compress does not change that.
--------------------------------------------------------------------------------
local function applyRemoveDuplicates(tArgs)
local iR, iK -- iR = reader, iK = killer
local hit = false

iR = 1
while iR iR do
if tArgs[iK] == tArgs[iR] then
hit = true
addPreviewMsg('Duplicate removed: ' .. tArgs[iR])
table.remove(tArgs, iK)
tTools.compressSparseArray(tArgs)
end
iK = iK - 1
end
tTools.compressSparseArray(tArgs)
iR = iR + 1
end

if hit then
addWarningCategory('D')
end
return tArgs
end

--------------------------------------------------------------------------------
-- applyOmitRules
--
-- returns edited table, with Omit phraseID's removed
-- Omit rule is per GHS_Rev9E_0.pdf (2021)
--------------------------------------------------------------------------------
local function applyOmitRules(tArgs)
local tRules = GHSdata['tOmitRules']
local hit = false

for keep, omit in pairs(tRules) do
if tTools.inArray(tArgs, omit) then
if tTools.inArray(tArgs, keep) then
hit = true
for i, k in pairs(tArgs) do
if k omit then
table.remove(tArgs, i)
end
end
addPreviewMsg(wlHelpPage('Omit Rules') .. ': keep ' .. keep .. ', omit ' .. omit)
end
end
end
if hit then
tTools.compressSparseArray(tArgs)
addWarningCategory('O')
end
return tArgs
end

--------------------------------------------------------------------------------
-- label H-phrases or P-phrases
--------------------------------------------------------------------------------
local function PHlabel()
if tArgName['setID'] 'GHS' then
return 'GHS phrases'
else
return tArgName['setID'] .. '-phrases'
end
end

--------------------------------------------------------------------------------
-- inMono
--
-- Use mono font-family (from: Template:Mono)
--------------------------------------------------------------------------------
local function inMono(s)
if s == nil then s = end
return .. s ..
end

--------------------------------------------------------------------------------
-- wlInlineTag
--
-- Returns [?] with wikilink to errormessage
--------------------------------------------------------------------------------
local function wlInlineTag(phraseID)
local sMsg
sMsg = '&91;'
.. wlHelpPage(PHlabel(), '?')
.. '&93;'
return sMsg
end

--------------------------------------------------------------------------------
-- errorPhraseIDnotFound
--
-- Returns single value when error (not found in list):
-- plain value + inline warning [?] (linked) + error cat (mainsp) + preview warning
--------------------------------------------------------------------------------
local function errorPhraseIDnotFound(phraseID)
if phraseID == nil then phraseID = end

local inlineTag = wlInlineTag(phraseID)
local previewMsg = wlHelpPage(PHlabel()) .. ': không tìm thấy \"' .. phraseID .. '\"'
addPreviewMsg(previewMsg)
addErrorCategory()

return phraseID .. inlineTag
end

--------------------------------------------------------------------------------
-- errorHPsetIDnotFound
--
-- setID H or P could not be found
--------------------------------------------------------------------------------
local function errorHPsetIDnotFound()
local sMsg
sMsg = wlHelpPage(, PHlabel())
.. ': không tìm thấy id "H" hoặc "P"'
.. ' (vui lòng sử dụng mẫu như "|H200" hoặc "|P300+P301")'
addPreviewMsg(sMsg)
addErrorCategory('?')
return showPreviewMsg()
end

--------------------------------------------------------------------------------
-- errorHPsetIDmissing
--
-- parameter |setid= to be used
--------------------------------------------------------------------------------
local function errorHPsetIDmissing()
local sMsg
sMsg = wlHelpPage( , PHlabel())
.. ': không tìm thấy id "H" hoặc "P",'
.. ' vui lòng sử dụng |setid=... (H hoặc P)'
addPreviewMsg(sMsg)
return
end

--------------------------------------------------------------------------------
-- formatPhraseAbbr
--
-- format phraseID and text, for abbr-form (infobox list form)
--------------------------------------------------------------------------------
local function formatPhraseAbbr(phraseID, sPhrase)
return
.. phraseID
..
end

--------------------------------------------------------------------------------
-- formatPhraseInline
--
-- format phraseID and text, for inline form (in sentence)
-- adds "quotes"
--------------------------------------------------------------------------------
local function formatPhraseInline(phraseID, sPhrase)
return inMono(phraseID) .. ': \"' .. sPhrase .. '\"'
end

--------------------------------------------------------------------------------
-- formatPhraseList
--
-- as inline, but no "quotes" added.
--------------------------------------------------------------------------------
local function formatPhraseList(phraseID, sPhrase)
return inMono(phraseID) .. ': ' .. sPhrase
end

--------------------------------------------------------------------------------
-- getSetID
--
-- Determines setID (expected either 'H' or 'P')
-- First route is: read |setid=
-- When |setid= is not set,
-- it looks for a first parameter that has an H of P prefix (in |P201|P202|...)
-- when not found, 'GHS' is retured
-- In one call, P and H numbers can not be mixed
-- so "|H201|P202|" will cause error "P202 not found" (... in H-list)
--------------------------------------------------------------------------------
local function getSetID(tArgs)
local setIDfound = 'GHS'
local paramsetID = tArgs['setid'] or nil

if (paramsetID ~= nil) and (paramsetID 'P' or paramsetID 'H') then
setIDfound = paramsetID
else
local initial = nil
for i, v in ipairs(tArgs) do
initial = mw.ustring.match(v, '^[PH]')
if initial ~=nil then
setIDfound = initial
break
end
end
end
return setIDfound
end

--------------------------------------------------------------------------------
-- getListType
--
-- Checks list format, including those from Module:List
--------------------------------------------------------------------------------
local function getListType(tArgs)
local listTypes = {
['abbr'] = true,
['bulleted'] = true,
['unbulleted'] = true,
['horizontal'] = true,
['ordered'] = true,
['horizontal_ordered'] = true,
['horizontal ordered'] = true,
['inline'] = true
}
local sListType = tArgs['listtype'] or 'abbr'

if sListType or sListType 'abbr' then
return 'abbr'
elseif listTypes[sListType] true then
if sListType 'horizontal ordered' then
sListType = 'horizontal_ordered'
end
return sListType
else
sListType = 'abbr'
end
return sListType
end

--------------------------------------------------------------------------------
-- getDoOmitRules
--------------------------------------------------------------------------------
local function getDoOmitRules(tArgs)
local b = yesno(tArgs['omit'], true)

if b == nil then b = true end

return yesno(b, true)
end

--------------------------------------------------------------------------------
-- prepareArgs
--
-- First: determine setID (from |setID= OR from prefixes in parameters)
-- Then: clean up & format phrase IDs (=unnamed parameters)
-- remove bad characters, create H/P pattern "H201", "P310+P302"
-- straight array, no nil's, sorted
--------------------------------------------------------------------------------
local function prepareArgs(tArgs)

tArgName['setID'] = getSetID(tArgs)
tArgName['listtype'] = getListType(tArgs)
tArgName['omit'] = getDoOmitRules(tArgs)

tArgs = tTools.compressSparseArray(tArgs) -- removes all named args
if string.len(tArgName['setID']) == 1 and tArgs > 0 then
for i, v in ipairs(tArgs) do
v = mw.text.decode(v)
v = mw.ustring.gsub(v, '[^%d%+A-Za-z]', )
v = mw.ustring.gsub(v, '^(%d)', tArgName['setID'] .. '%1')
v = mw.ustring.gsub(v, '%+(%d)', '+' .. tArgName['setID'] .. '%1')
tArgs[i] = v
end
table.sort(tArgs)
end
return tArgs
end

--------------------------------------------------------------------------------
-- listAll
--
-- Returns wikitable rows for each phrase id.
-- requires |setID=P/H
-- returns full list, all phrases, for a setID
-- 2-columns wikitable, sorted, sortable, anchor like "H201" for each
--------------------------------------------------------------------------------
function r.listAll(frame)
local newArgs = getArgs(frame)
local tL = {}

prepareArgs(newArgs)

local tRead
if tArgName['setID'] == 'H' then
tRead = GHSdata['Hphrases']
elseif tArgName['setID'] == 'P' then
tRead = GHSdata['Pphrases']
else
errorHPsetIDmissing()
return showPreviewMsg()
end

-- Intermediate table t2 to maintain order; read from original table (/data)
local t2 = {}
local iPh
for s, v in pairs(tRead) do
iPh = tonumber(mw.ustring.match(s, '[PH](%d%d%d)'))
if string.len(s) > 4 then
iPh = tTools.size(t2) + 1
end
table.insert(t2, iPh, s)
end
t2 = tTools.compressSparseArray(t2)
table.sort(t2)

local sTR, v, sAnchor
-- i = array index, s = phraseID, v = phrase text
for i, s in ipairs(t2) do
v = tRead[s]
sAnchor =
sTR = '|- ' .. sAnchor .. '\n| datasortvalue="' .. i .. '" | ' .. s .. ' || ' .. v
table.insert(tL, sTR)
end

return table.concat(tL, '\n')
end

--------------------------------------------------------------------------------
-- numberOfPhrases
--
-- Documentation
-- requires |setID=H/P
-- Returns number of phrases, in format
-- "GHS H-phrases (123)"
--------------------------------------------------------------------------------
function r.numberOfPhrases(frame)
local newArgs = getArgs(frame)

prepareArgs(newArgs)

local iT
if tArgName['setID'] == 'H' then
iT = tTools.size(GHSdata['Hphrases'])
elseif tArgName['setID'] == 'P' then
iT = tTools.size(GHSdata['Pphrases'])
else
errorHPsetIDmissing()
return showPreviewMsg()
end

return 'GHS ' .. PHlabel() .. ' (' .. tostring(iT) .. ')'
end

--------------------------------------------------------------------------------
-- listOmitRules
--
-- self-documentation
--------------------------------------------------------------------------------
function r.listOmitRules()
local tRules = GHSdata['tOmitRules']
local tL = {}
local s

s = wlHelpPage('Omit Rules')
.. ': when the keep ID is present, do not show the omit ID phrase'
table.insert(tL, s)
for keep, omit in pairs (tRules) do
s = '&bull; keep ' .. inMono(keep) .. ', omit ' .. inMono(omit)
table.insert(tL, s)
end
return table.concat(tL, )
end

--------------------------------------------------------------------------------
-- _main
--
-- processes setID (H, P) and phrase codes
-- error: setID not P, H
-- code not found
-- cannot mix H and P phrases
-- reads phrases from /data H or P phrases tables
-- formats phrase (abbreviation, abbr-title, phraseID)
--------------------------------------------------------------------------------
function r._main(tArgs)

tArgs = prepareArgs(tArgs)

if tArgs 0 then
return showPreviewMsg() -- no content
elseif tArgName['setID'] 'GHS' then
return errorHPsetIDnotFound()
end

tArgs = applyRemoveDuplicates(tArgs)
if tArgName['omit'] then
tArgs = applyOmitRules(tArgs)
end

local formatterF
if tArgName['listtype'] == 'abbr' then
formatterF = formatPhraseAbbr
elseif tArgName['listtype'] == 'inline' then
formatterF = formatPhraseInline
else --- Module:List options
formatterF = formatPhraseList
end

local tReadD = {}
if tArgName['setID'] == 'H' then
tReadD = GHSdata['Hphrases']
elseif tArgName['setID'] == 'P' then
tReadD = GHSdata['Pphrases']
else
return showPreviewMsg()
end

local sPhrase
local tR = {}
for i, v in ipairs(tArgs) do
sPhrase = tReadD[v]
if sPhrase nil then
table.insert(tR, errorPhraseIDnotFound(tostring(v)))
else
table.insert(tR, formatterF(v, sPhrase))
end
end

if tArgName['listtype'] 'abbr' then
return table.concat(tR, ', ') .. showPreviewMsg()
elseif tArgName['listtype'] == 'inline' then
return table.concat(tR, ', ') .. showPreviewMsg()
else
local mList = require('Module:List')
return mList[tArgName['listtype']](tR) .. showPreviewMsg()
end
end

--------------------------------------------------------------------------------
-- main
--
-- handles template input frame, then calls generic _main() function
-- To be invoked from
--------------------------------------------------------------------------------
function r.main(frame)
local newArgs = getArgs(frame)
return r._main(newArgs)
end

return r