Module:TemplatePar: Difference between revisions
From Neodyland Wiki
More actions
m Leyo moved page Module:Modul:TemplatePar to Module:TemplatePar without leaving a redirect: fix |
mNo edit summary |
||
| Line 1: | Line 1: | ||
--[=[ TemplatePar 2013-07- | --[=[ TemplatePar 2013-07-09Template parameter utility* assert* check* count | ||
* countNotEmpty* downcase* match* valid* verify* TemplatePar()]=] | |||
* assert | -- Module globalslocal TemplatePar = { } | ||
* check | local messagePrefix = "lua-module-TemplatePar-"local l10nDef = {} | ||
* count | |||
* countNotEmpty | |||
* downcase | |||
* match | |||
* valid | |||
* verify | |||
* TemplatePar() | |||
]=] | |||
-- Module | |||
local messagePrefix = "lua-module-TemplatePar-" | |||
local l10nDef = {} | |||
l10nDef[ "en" ] = { | l10nDef[ "en" ] = { | ||
badPattern = "#invoke:TemplatePar pattern syntax error", | badPattern = "#invoke:TemplatePar pattern syntax error", | ||
| Line 33: | Line 18: | ||
undefined = "Error in template * mandatory parameter missing", | undefined = "Error in template * mandatory parameter missing", | ||
unknown = "Error in template * unknown parameter name", | unknown = "Error in template * unknown parameter name", | ||
unknownRule = "#invoke:TemplatePar unknown rule" | unknownRule = "#invoke:TemplatePar unknown rule"}l10nDef[ "de" ] = { | ||
} | |||
l10nDef[ "de" ] = { | |||
badPattern = "#invoke:TemplatePar Syntaxfehler des pattern", | badPattern = "#invoke:TemplatePar Syntaxfehler des pattern", | ||
dupOpt = "#invoke:TemplatePar Optionsparameter wiederholt", | dupOpt = "#invoke:TemplatePar Optionsparameter wiederholt", | ||
| Line 50: | Line 33: | ||
undefined = "Fehler bei Vorlage * Pflichtparameter fehlt", | undefined = "Fehler bei Vorlage * Pflichtparameter fehlt", | ||
unknown = "Fehler bei Vorlage * Parametername unbekannt", | unknown = "Fehler bei Vorlage * Parametername unbekannt", | ||
unknownRule = "#invoke:TemplatePar Unbekannte Regel" | unknownRule = "#invoke:TemplatePar Unbekannte Regel"}local Patterns = { | ||
} | [ "ASCII" ] = "^[ -~]*$", [ "ASCII+" ] = "^[ -~]+$", | ||
local Patterns = { | [ "ASCII+1" ] = "^[!-~]+$", [ "n" ] = "^%-?[0-9]*$", | ||
[ "ASCII" ] = "^[ -~]*$", | |||
[ "ASCII+1" ] = "^[!-~]+$", | |||
[ "n>0" ] = "^[0-9]*[1-9][0-9]*$", | [ "n>0" ] = "^[0-9]*[1-9][0-9]*$", | ||
[ "N+" ] = "^%-?[1-9][0-9]*$", | [ "N+" ] = "^%-?[1-9][0-9]*$", [ "N>0" ] = "^[1-9][0-9]*$", | ||
[ "x" ] = "^[0-9A-Fa-f]*$", [ "x+" ] = "^[0-9A-Fa-f]+$", | |||
[ "x" ] = "^[0-9A-Fa-f]*$", | [ "X" ] = "^[0-9A-F]*$", [ "X+" ] = "^[0-9A-F]+$", | ||
[ "X" ] = "^[0-9A-F]*$", | |||
[ "0,0" ] = "^%-?[0-9]*,?[0-9]*$", | [ "0,0" ] = "^%-?[0-9]*,?[0-9]*$", | ||
[ "0,0+" ] = "^%-?[0-9]+,[0-9]+$", | [ "0,0+" ] = "^%-?[0-9]+,[0-9]+$", | ||
| Line 72: | Line 48: | ||
[ ".0+" ] = "^%-?[0-9]*%.?[0-9]+$", | [ ".0+" ] = "^%-?[0-9]*%.?[0-9]+$", | ||
[ "ID" ] = "^[A-Za-z]?[A-Za-z_0-9]*$", | [ "ID" ] = "^[A-Za-z]?[A-Za-z_0-9]*$", | ||
[ "ID+" ] = "^[A-Za-z][A-Za-z_0-9]*$", | [ "ID+" ] = "^[A-Za-z][A-Za-z_0-9]*$", [ "ABC" ] = "^[A-Z]*$", | ||
[ "ABC+" ] = "^[A-Z]+$", [ "Abc" ] = "^[A-Z]*[a-z]*$", | |||
[ "ABC+" ] = "^[A-Z]+$", | [ "Abc+" ] = "^[A-Z][a-z]+$", [ "abc" ] = "^[a-z]*$", | ||
[ "abc+" ] = "^[a-z]+$", [ "aBc+" ] = "^[a-z]+[A-Z][A-Za-z]*$", | |||
[ "Abc+" ] = "^[A-Z][a-z]+$", | |||
[ "abc+" ] = "^[a-z]+$", | |||
[ "base64" ] = "^[A-Za-z0-9%+/]*$", | [ "base64" ] = "^[A-Za-z0-9%+/]*$", | ||
[ "base64+" ] = "^[A-Za-z0-9%+/]+$", | [ "base64+" ] = "^[A-Za-z0-9%+/]+$", [ "aa" ] = "[%a%a].*[%a%a]", | ||
[ "pagename" ] = string.format( "^[^#<>%%[%%]|{}%s%-s%s]+$", | [ "pagename" ] = string.format( "^[^#<>%%[%%]|{}%s%-s%s]+$", | ||
1, 31, 127 ), | 1, 31, 127 ), [ "+" ] = "%S"} | ||
local patternCJK = falselocal function containsCJK( s ) | |||
} | -- Is any CJK character present? -- Precondition: -- s -- string | ||
local patternCJK = | -- Postcondition: -- Return false iff no CJK present -- Uses: | ||
-- >< patternCJK -- mw.ustring.char() | |||
-- mw.ustring.match() local r = false if not patternCJK then | |||
-- Is any CJK character present? | |||
-- Postcondition: | |||
-- >< patternCJK | |||
-- mw.ustring.match() | |||
patternCJK = mw.ustring.char( 91, | patternCJK = mw.ustring.char( 91, | ||
13312, 45, 40959, | 13312, 45, 40959, | ||
131072, 45, 178207, | 131072, 45, 178207, | ||
93 ) | 93 ) end | ||
if mw.ustring.match( s, patternCJK ) then r = true end return r | |||
if mw.ustring.match( s, patternCJK ) then | end -- containsCJK()local function factory( say ) | ||
-- Retrieve localized message string in content language -- Precondition: | |||
-- say -- string; message ID -- Postcondition: | |||
-- Return some message string -- Uses: -- > messagePrefix | |||
end -- containsCJK() | -- > l10nDef -- mw.language.getContentLanguage() | ||
local function factory( say ) | |||
-- Retrieve localized message string in content language | |||
-- say -- string; message ID | |||
-- Return some message string | |||
-- > l10nDef | |||
-- mw.message.new() | -- mw.message.new() | ||
local c = mw.language.getContentLanguage():getCode() | local c = mw.language.getContentLanguage():getCode() | ||
local m = mw.message.new( messagePrefix .. say ) | local m = mw.message.new( messagePrefix .. say ) local r = false | ||
if m:isBlank() then local l10n = l10nDef[ c ] if not l10n then | |||
if m:isBlank() then | l10n = l10nDef[ "en" ] end r = l10n[ say ] else | ||
m:inLanguage( c ) r = m:plain() end if not r then | |||
r = "(((".. say .. ")))" end return rend -- factory() | |||
l10n = l10nDef[ "en" ] | |||
m:inLanguage( c ) | |||
r = "(((".. say .. ")))" | |||
local function failsafe( story, scan ) | local function failsafe( story, scan ) | ||
-- Test for match (possibly user-defined with syntax error) | -- Test for match (possibly user-defined with syntax error) | ||
-- Precondition: | -- Precondition: -- story -- string; parameter value | ||
-- scan -- string; pattern -- Postcondition: | |||
-- scan -- string; pattern | -- Return nil, if not matching, else non-nil -- Uses: | ||
-- mw.ustring.match() return mw.ustring.match( story, scan ) | |||
-- Return nil, if not matching, else non-nil | end -- failsafe()local function failure( spec, suspect, options ) | ||
-- Submit localized error message -- Precondition: | |||
-- mw.ustring.match() | |||
end -- failsafe() | |||
local function failure( spec, suspect, options ) | |||
-- Submit localized error message | |||
-- spec -- string; message ID | -- spec -- string; message ID | ||
-- suspect -- string or nil; additional information | -- suspect -- string or nil; additional information | ||
-- options -- table or nil; optional details | -- options -- table or nil; optional details | ||
-- options.template | -- options.template -- Postcondition: | ||
-- Return string -- Uses: -- factory() | |||
-- Return string | local r = factory( spec ) if type( options ) == "table" then | ||
local r = factory( spec ) | |||
if type( options.template ) == "string" then | if type( options.template ) == "string" then | ||
if #options.template > 0 then | if #options.template > 0 then | ||
r = r .. " (" .. options.template .. ")" | r = r .. " (" .. options.template .. ")" end | ||
end end if suspect then r = r .. ": " .. suspect end | |||
end | return rend -- failure()local function fault( store, key ) | ||
-- Add key to collection string and insert separator -- Precondition: | |||
return | |||
local function fault( store, key ) | |||
-- Add key to collection string and insert separator | |||
-- store -- string or nil or false; collection string | -- store -- string or nil or false; collection string | ||
-- key -- string or number; to be appended | -- key -- string or number; to be appended -- Postcondition: | ||
-- Return string; extended local r local s | |||
-- Return string; extended | if type( key ) == "number" then s = tostring( key ) else | ||
s = key end if store then r = store .. "; " .. s else | |||
r = s end return rend -- fault() | |||
if type( key ) == "number" then | local function feasible( analyze, options, abbr ) -- Check content of a value | ||
-- Precondition: -- analyze -- string to be analyzed | |||
s = key | |||
r = s | |||
local function feasible( analyze, options, abbr ) | |||
-- Precondition: | |||
-- options -- table or nil; optional details | -- options -- table or nil; optional details | ||
-- options.pattern | -- options.pattern -- options.key | ||
-- options.say | -- options.say | ||
-- abbr -- true: abbreviated error message | -- abbr -- true: abbreviated error message -- Postcondition: | ||
-- Return string with error message as configured; | -- Return string with error message as configured; | ||
-- false if valid or no answer permitted | -- false if valid or no answer permitted -- Uses: | ||
-- > Patterns -- failure() -- mw.text.trim() | |||
-- > Patterns | -- failsafe() -- containsCJK() local r = false | ||
local s = false local show = nil local scan = false | |||
if type( options.pattern ) == "string" then if options.key then | |||
-- failsafe() | r = failure( "dupRule", false, options ) else | ||
scan = options.pattern end else | |||
local s = false | |||
if type( options.pattern ) == "string" then | |||
r = failure( "dupRule", false, options ) | |||
scan = options.pattern | |||
if type( options.key ) == "string" then | if type( options.key ) == "string" then | ||
s = mw.text.trim( options.key ) | s = mw.text.trim( options.key ) else s = "+" | ||
end if s ~= "*" then scan = Patterns[ s ] end | |||
end | |||
if type( scan ) == "string" then | if type( scan ) == "string" then | ||
if s == "n" or s == "0,0" or s == "0.0" then | if s == "n" or s == "0,0" or s == "0.0" then | ||
if not analyze:match( "[0-9]" ) then | if not analyze:match( "[0-9]" ) then | ||
scan = false | scan = false if options.say then | ||
show = "'" .. options.say .. "'" end | |||
show = "'" .. options.say .. "'" | if abbr then r = show | ||
if abbr then | |||
else | else | ||
r = failure( "invalid", show, options ) | r = failure( "invalid", show, options ) | ||
end | end end end | ||
elseif s ~= "*" then | elseif s ~= "*" then | ||
local op, n, plus = s:match( "([<!>]=?)([-0-9][%S]*)(+?)" ) | local op, n, plus = s:match( "([<!>]=?)([-0-9][%S]*)(+?)" ) | ||
if op then | if op then n = tonumber( n ) if n then | ||
local i = tonumber( analyze ) if i then | |||
local i = tonumber( analyze ) | |||
if op == "<" then | if op == "<" then | ||
i = ( i < n ) | i = ( i < n ) | ||
| Line 280: | Line 143: | ||
i = ( i >= n ) | i = ( i >= n ) | ||
elseif op == "!=" then | elseif op == "!=" then | ||
i = ( i ~= n ) | i = ( i ~= n ) else | ||
n = false end | |||
n = false | end if not i then | ||
r = "invalid" end | |||
end | elseif plus then r = "undefined" | ||
end end if not n and not r then | |||
r = "invalid" | r = "unknownRule" end if r then | ||
elseif plus then | |||
end | |||
r = "unknownRule" | |||
if options.say then | if options.say then | ||
show = "'" .. options.say .. "' " .. s | show = "'" .. options.say .. "' " .. s else | ||
show = s end if abbr then | |||
show = s | r = show else | ||
r = failure( r, show, options ) end | |||
end end end if scan then | |||
r = show | local legal, got = pcall( failsafe, analyze, scan ) if legal then | ||
if not got then if s == "aa" then | |||
r = failure( r, show, options ) | got = containsCJK( analyze ) end | ||
if not got then if options.say then | |||
end | show = "'" .. options.say .. "'" end | ||
if abbr then r = show | |||
local legal, got = pcall( failsafe, analyze, scan ) | |||
if not got then | |||
got = containsCJK( analyze ) | |||
if not got then | |||
show = "'" .. options.say .. "'" | |||
if abbr then | |||
else | else | ||
r = failure( "invalid", show, options ) | r = failure( "invalid", show, options ) | ||
end | end end end else | ||
r = failure( "badPattern", | r = failure( "badPattern", | ||
scan .. " *** " .. got, | scan .. " *** " .. got, | ||
options ) | options ) end end return r | ||
end -- feasible()local function fed( haystack, needle ) | |||
-- Find needle in haystack map -- Precondition: | |||
end -- feasible() | |||
local function fed( haystack, needle ) | |||
-- Find needle in haystack map | |||
-- haystack -- table; map of key values | -- haystack -- table; map of key values | ||
-- needle -- any; identifier | -- needle -- any; identifier -- Postcondition: | ||
-- Return true iff found local k, v | |||
-- Return true iff found | for k, v in pairs( haystack ) do if k == needle then | ||
return true end end -- for k, v return false | |||
for k, v in pairs( haystack ) do | end -- fed()local function fetch( light, options ) | ||
-- Return regular table with all parameters -- Precondition: | |||
return true | |||
end -- fed() | |||
local function fetch( light, options ) | |||
-- Return regular table with all parameters | |||
-- light -- true: template transclusion; false: #invoke | -- light -- true: template transclusion; false: #invoke | ||
-- options -- table; optional details | -- options -- table; optional details -- options.low | ||
-- Postcondition: -- Return table; whitespace-only values as false | |||
-- Postcondition: | -- Uses: -- TemplatePar.downcase() -- mw.getCurrentFrame() | ||
-- frame:getParent() local g, k, v local r = { } | |||
-- Uses: | if options.low then g = TemplatePar.downcase( options ) else | ||
g = mw.getCurrentFrame() if light then | |||
g = g:getParent() end g = g.args end | |||
-- frame:getParent() | if type( g ) == "table" then r = { } | ||
for k, v in pairs( g ) do if type( v ) == "string" then | |||
if v:match( "^%s*$" ) then v = false | |||
if options.low then | end else v = false end | ||
if type( k ) == "number" then k = tostring( k ) | |||
end r[ k ] = v end -- for k, v else | |||
g = mw.getCurrentFrame() | r = g end return rend -- fetch() | ||
g = g:getParent() | |||
if type( g ) == "table" then | |||
for k, v in pairs( g ) do | |||
if v:match( "^%s*$" ) then | |||
end | |||
if type( k ) == "number" then | |||
end | |||
r = g | |||
local function figure( append, options ) | local function figure( append, options ) | ||
-- Extend options by rule from #invoke strings | -- Extend options by rule from #invoke strings -- Precondition: | ||
-- append -- string or nil; requested rule | -- append -- string or nil; requested rule | ||
-- options -- table; details | -- options -- table; details -- ++ .key | ||
-- ++ .pattern -- Postcondition: | |||
-- ++ .pattern | -- Return sequence table local r = options | ||
-- Return sequence table | |||
if type( append ) == "string" then | if type( append ) == "string" then | ||
local story = mw.text.trim( append ) | local story = mw.text.trim( append ) | ||
| Line 419: | Line 205: | ||
sub = sub:gsub( "%%%(%(", "{{" ) | sub = sub:gsub( "%%%(%(", "{{" ) | ||
sub = sub:gsub( "%%%)%)", "}}" ) | sub = sub:gsub( "%%%)%)", "}}" ) | ||
options.pattern = sub | options.pattern = sub options.key = nil else | ||
options.key = story options.pattern = nil end | |||
end return rend -- figure()local function fill( specified ) | |||
options.key = story | -- Split requirement string separated by '=' -- Precondition: | ||
end | |||
local function fill( specified ) | |||
-- Split requirement string separated by '=' | |||
-- specified -- string or nil; requested parameter set | -- specified -- string or nil; requested parameter set | ||
-- Postcondition: | -- Postcondition: -- Return sequence table -- Uses: | ||
-- mw.text.split() local r if specified then local i, s | |||
r = mw.text.split( specified, "%s*=%s*" ) for i = #r, 1, -1 do | |||
-- mw.text.split() | s = r[ i ] if #s == 0 then | ||
table.remove( r, i ) end end -- for i, -1 | |||
else r = { } end return rend -- fill() | |||
local function finalize( submit, options ) -- Finalize message | |||
r = mw.text.split( specified, "%s*=%s*" ) | |||
s = r[ i ] | |||
table.remove( r, i ) | |||
else | |||
local function finalize( submit, options ) | |||
-- Precondition: | -- Precondition: | ||
-- submit -- string or false or nil; non-empty error message | -- submit -- string or false or nil; non-empty error message | ||
-- options -- table or nil; optional details | -- options -- table or nil; optional details | ||
-- options.noError | -- options.noError -- options.cat | ||
-- options.template -- Postcondition: | |||
-- options.template | -- Return string or false -- Uses: -- factory() | ||
local r = false if submit then local opt, s | |||
-- Return string or false | if type( options ) == "table" then opt = options else | ||
opt = { } end if opt.noError then | |||
local r = false | |||
if type( options ) == "table" then | |||
opt = { } | |||
if not opt.cat then | if not opt.cat then | ||
r = submit .. " " .. factory( "noErrorCat" ) | r = submit .. " " .. factory( "noErrorCat" ) end | ||
else r = submit end if r then | |||
else | r = "<span class='error'>" .. r .. "</span>" end | ||
s = opt.cat if type( s ) == "string" then | |||
if not r then r = "" end | |||
r = "<span class='error'>" .. r .. "</span>" | |||
s = opt.cat | |||
if not r then | |||
if s:find( "@@@" ) then | if s:find( "@@@" ) then | ||
if type( opt.template ) == "string" then | if type( opt.template ) == "string" then | ||
s = s:gsub( "@@@", opt.template ) | s = s:gsub( "@@@", opt.template ) end | ||
end r = r .. "[[Category:" .. s .. "]]" end end | |||
end | return rend -- finalize()local function finder( haystack, needle ) | ||
-- Find needle in haystack sequence -- Precondition: | |||
return | |||
local function finder( haystack, needle ) | |||
-- Find needle in haystack sequence | |||
-- haystack -- table; sequence of key names, downcased if low | -- haystack -- table; sequence of key names, downcased if low | ||
-- needle -- any; key name | -- needle -- any; key name -- Postcondition: | ||
-- Return true iff found local i for i = 1, #haystack do | |||
-- Return true iff found | if haystack[ i ] == needle then return true end | ||
end -- for i return falseend -- finder() | |||
local function fix( valid, duty, got, options ) -- Perform parameter analysis | |||
if haystack[ i ] == needle then | |||
end -- for i | |||
local function fix( valid, duty, got, options ) | |||
-- Precondition: | -- Precondition: | ||
-- valid -- table; unique sequence of known parameters | -- valid -- table; unique sequence of known parameters | ||
-- duty -- table; sequence of mandatory parameters | -- duty -- table; sequence of mandatory parameters | ||
-- got -- table; sequence of current parameters | -- got -- table; sequence of current parameters | ||
-- options -- table or nil; optional details | -- options -- table or nil; optional details -- Postcondition: | ||
-- Return string as configured; empty if valid -- Uses: | |||
-- Return string as configured; empty if valid | -- finder() -- fault() -- failure() -- fed() | ||
local k, v local r = false for k, v in pairs( got ) do | |||
-- finder() | if not finder( valid, k ) then r = fault( r, k ) end | ||
end -- for k, v if r then | |||
local k, v | |||
if not finder( valid, k ) then | |||
end -- for k, v | |||
r = failure( "unknown", "'" .. r .. "'", options ) | r = failure( "unknown", "'" .. r .. "'", options ) | ||
else -- all names valid | else -- all names valid local i, s for i = 1, #duty do | ||
s = duty[ i ] if not fed( got, s ) then | |||
r = fault( r, s ) end end -- for i | |||
s = duty[ i ] | if r then r = failure( "undefined", r, options ) | ||
else -- all mandatory present for i = 1, #duty do | |||
r = fault( r, s ) | s = duty[ i ] if not got[ s ] then | ||
r = fault( r, s ) end end -- for i | |||
if r then r = failure( "empty", r, options ) | |||
if r then | end end end return rend -- fix() | ||
else -- all mandatory present | |||
s = duty[ i ] | |||
r = fault( r, s ) | |||
if r then | |||
end | |||
local function flat( collection, options ) | local function flat( collection, options ) | ||
-- Return all table elements with downcased string | -- Return all table elements with downcased string -- Precondition: | ||
-- collection -- table; k=v pairs | -- collection -- table; k=v pairs | ||
-- options -- table or nil; optional messaging details | -- options -- table or nil; optional messaging details | ||
-- Postcondition: | -- Postcondition: | ||
-- Return table, may be empty; or string with error message. | -- Return table, may be empty; or string with error message. -- Uses: | ||
-- mw.ustring.lower() -- fault() -- failure() | |||
-- mw.ustring.lower() | local k, v local r = { } local e = false | ||
for k, v in pairs( collection ) do if type ( k ) == "string" then | |||
k = mw.ustring.lower( k ) if r[ k ] then | |||
local k, v | e = fault( e, k ) end end r[ k ] = v | ||
end -- for k, v if e then r = failure( "multiSpell", e, options ) | |||
end return rend -- flat()local function fold( options ) | |||
for k, v in pairs( collection ) do | |||
k = mw.ustring.lower( k ) | |||
e = fault( e, k ) | |||
end -- for k, v | |||
end | |||
local function fold( options ) | |||
-- Merge two tables, create new sequence if both not empty | -- Merge two tables, create new sequence if both not empty | ||
-- Precondition: | -- Precondition: -- options -- table; details | ||
-- options.mandatory sequence to keep unchanged | -- options.mandatory sequence to keep unchanged | ||
-- options.optional sequence to be appended | -- options.optional sequence to be appended | ||
-- options.low downcased expected | -- options.low downcased expected | ||
-- Postcondition: | -- Postcondition: -- Return merged table, or message string if error | ||
-- Uses: -- finder() -- fault() -- failure() | |||
-- Uses: | -- flat() local i, e, r, s local base = options.mandatory | ||
local extend = options.optional if #base == 0 then | |||
if #extend == 0 then r = { } else | |||
r = extend end else if #extend == 0 then | |||
-- flat() | r = base else e = false | ||
for i = 1, #extend do s = extend[ i ] | |||
if finder( base, s ) then e = fault( e, s ) | |||
local extend = options.optional | end end -- for i if e then | ||
r = failure( "dupOpt", e, options ) else | |||
if #extend == 0 then | r = { } for i = 1, #base do | ||
table.insert( r, base[ i ] ) end -- for i | |||
r = extend | |||
r = base | |||
for i = 1, #extend do | |||
if finder( base, s ) then | |||
end | |||
r = failure( "dupOpt", e, options ) | |||
r = { } | |||
table.insert( r, base[ i ] ) | |||
for i = 1, #extend do | for i = 1, #extend do | ||
table.insert( r, extend[ i ] ) | table.insert( r, extend[ i ] ) end -- for i | ||
end end end | |||
end | if options.low and type( r ) == "table" then r = flat( r, options ) | ||
end return rend -- fold()local function form( light, options ) | |||
-- Run parameter analysis on current environment -- Precondition: | |||
if options.low and type( r ) == "table" then | |||
end | |||
local function form( light, options ) | |||
-- Run parameter analysis on current environment | |||
-- light -- true: template transclusion; false: #invoke | -- light -- true: template transclusion; false: #invoke | ||
-- options -- table or nil; optional details | -- options -- table or nil; optional details | ||
-- options.mandatory | -- options.mandatory -- options.optional | ||
-- Postcondition: -- Return string with error message as configured; | |||
-- Postcondition: | -- false if valid -- Uses: -- fold() -- fetch() | ||
-- fix() -- finalize() local duty, r | |||
-- false if valid | |||
-- fix() | |||
if type( options ) == "table" then | if type( options ) == "table" then | ||
if type( options.mandatory ) ~= "table" then | if type( options.mandatory ) ~= "table" then | ||
options.mandatory = { } | options.mandatory = { } end duty = options.mandatory | ||
if type( options.optional ) ~= "table" then | if type( options.optional ) ~= "table" then | ||
options.optional = { } | options.optional = { } end r = fold( options ) else | ||
options = { } duty = { } r = { } end | |||
if type( r ) == "table" then local got = fetch( light, options ) | |||
options = { } | |||
if type( r ) == "table" then | |||
if type( got ) == "table" then | if type( got ) == "table" then | ||
r = fix( r, duty, got, options ) | r = fix( r, duty, got, options ) else r = got | ||
end end return finalize( r, options )end -- form() | |||
local function format( analyze, options ) -- Check validity of a value | |||
end | -- Precondition: -- analyze -- string to be analyzed | ||
end -- form() | |||
local function format( analyze, options ) | |||
-- Precondition: | |||
-- options -- table or nil; optional details | -- options -- table or nil; optional details | ||
-- options.say | -- options.say -- options.min | ||
-- options.max -- Postcondition: | |||
-- options.max | |||
-- Return string with error message as configured; | -- Return string with error message as configured; | ||
-- false if valid or no answer permitted | -- false if valid or no answer permitted -- Uses: | ||
-- feasible() -- failure() | |||
-- feasible() | local r = feasible( analyze, options, false ) local show | ||
local r = feasible( analyze, options, false ) | |||
if options.min and not r then | if options.min and not r then | ||
if type( options.min ) == "number" then | if type( options.min ) == "number" then | ||
| Line 726: | Line 334: | ||
.. " > " .. | .. " > " .. | ||
tostring( options.max ), | tostring( options.max ), | ||
options ) | options ) end end | ||
if #analyze < options.min and not r then | if #analyze < options.min and not r then | ||
show = " <" .. options.min | show = " <" .. options.min if options.say then | ||
show = show .. " '" .. options.say .. "'" end | |||
show = show .. " '" .. options.say .. "'" | r = failure( "tooShort", show, options ) end | ||
else r = failure( "invalidPar", "min", options ) end | |||
r = failure( "tooShort", show, options ) | end if options.max and not r then | ||
else | |||
end | |||
if type( options.max ) == "number" then | if type( options.max ) == "number" then | ||
if #analyze > options.max then | if #analyze > options.max then | ||
show = " >" .. options.max | show = " >" .. options.max if options.say then | ||
show = show .. " '" .. options.say .. "'" end | |||
show = show .. " '" .. options.say .. "'" | r = failure( "tooLong", show, options ) end | ||
else r = failure( "invalidPar", "max", options ) end | |||
r = failure( "tooLong", show, options ) | end return rend -- format() | ||
else | |||
end | |||
local function formatted( assignment, access, options ) | local function formatted( assignment, access, options ) | ||
-- Check validity of one particular parameter in a collection | -- Check validity of one particular parameter in a collection | ||
-- Precondition: | -- Precondition: -- assignment -- collection | ||
-- access -- id of parameter in collection | -- access -- id of parameter in collection | ||
-- options -- table or nil; optional details | -- options -- table or nil; optional details -- Postcondition: | ||
-- Return string with error message as configured; | -- Return string with error message as configured; | ||
-- false if valid or no answer permitted | -- false if valid or no answer permitted -- Uses: | ||
-- format() -- failure() local r = false | |||
-- format() | |||
if type( assignment ) == "table" then | if type( assignment ) == "table" then | ||
local story = assignment.args[ access ] | local story = assignment.args[ access ] | ||
if type( story ) == "string" then | if type( story ) == "string" then | ||
if type( options ) ~= "table" then | if type( options ) ~= "table" then options = { } | ||
end options.say = access | |||
end | r = format( story, options ) else | ||
r = failure( "invalid", access, options ) end end | |||
r = format( story, options ) | return rend -- formatted()local function furnish( frame, action ) | ||
-- Prepare #invoke evaluation of .assert() or .valid() -- Precondition: | |||
r = failure( "invalid", access, options ) | |||
return | |||
local function furnish( frame, action ) | |||
-- Prepare #invoke evaluation of .assert() or .valid() | |||
-- frame -- object; #invoke environment | -- frame -- object; #invoke environment | ||
-- action -- "assert" or "valid" | -- action -- "assert" or "valid" -- Postcondition: | ||
-- Return string with error message or "" -- Uses: -- form() | |||
-- Return string with error message or "" | -- mw.text.trim() -- failure() -- TemplatePar.assert() | ||
-- TemplatePar.valid() local options = { mandatory = { "1" }, | |||
-- mw.text.trim() | |||
-- TemplatePar.valid() | |||
optional = { "2", | optional = { "2", | ||
"cat", | "cat", | ||
| Line 810: | Line 378: | ||
"template" }, | "template" }, | ||
template = "#invoke:TemplatePar|".. action .. "|" | template = "#invoke:TemplatePar|".. action .. "|" | ||
} | } local r = form( false, options ) if not r then | ||
local s options = { cat = frame.args.cat, | |||
local s | |||
low = frame.args.low, | low = frame.args.low, | ||
noError = frame.args.noError, | noError = frame.args.noError, | ||
template = frame.args.template | template = frame.args.template } | ||
options = figure( frame.args[ 2 ], options ) | options = figure( frame.args[ 2 ], options ) | ||
if type( frame.args.min ) == "string" then | if type( frame.args.min ) == "string" then | ||
s = frame.args.min:match( "^%s*([0-9]+)%s*$" ) | s = frame.args.min:match( "^%s*([0-9]+)%s*$" ) if s then | ||
options.min = tonumber( s ) else | |||
options.min = tonumber( s ) | |||
r = failure( "invalidPar", | r = failure( "invalidPar", | ||
"min=" .. frame.args.min, | "min=" .. frame.args.min, | ||
options ) | options ) end end | ||
if type( frame.args.max ) == "string" then | if type( frame.args.max ) == "string" then | ||
s = frame.args.max:match( "^%s*([1-9][0-9]*)%s*$" ) | s = frame.args.max:match( "^%s*([1-9][0-9]*)%s*$" ) | ||
if s then | if s then options.max = tonumber( s ) else | ||
r = failure( "invalidPar", | r = failure( "invalidPar", | ||
"max=" .. frame.args.max, | "max=" .. frame.args.max, | ||
options ) | options ) end end | ||
if r then r = finalize( r, options ) else | |||
s = frame.args[ 1 ] or "" r = tonumber( s ) | |||
if r then | if ( r ) then s = r end | ||
r = TemplatePar[ action ]( s, options ) end end | |||
return r or ""end -- furnish() | |||
s = frame.args[ 1 ] or "" | |||
if ( r ) then | |||
r = TemplatePar[ action ]( s, options ) | |||
return r or "" | |||
end -- furnish() | |||
TemplatePar.assert = function ( analyze, append, options ) | TemplatePar.assert = function ( analyze, append, options ) | ||
-- Perform parameter analysis on a single string | -- Perform parameter analysis on a single string -- Precondition: | ||
-- analyze -- string to be analyzed | -- analyze -- string to be analyzed | ||
-- append -- string: append error message, prepending <br /> | -- append -- string: append error message, prepending <br /> | ||
-- false or nil: throw error with message | -- false or nil: throw error with message | ||
-- options -- table; optional details | -- options -- table; optional details -- Postcondition: | ||
-- Return string with error message as configured; | -- Return string with error message as configured; | ||
-- false if valid | -- false if valid -- Uses: -- format() | ||
local r = format( analyze, options ) if ( r ) then | |||
local r = format( analyze, options ) | |||
if ( type( append ) == "string" ) then | if ( type( append ) == "string" ) then | ||
if ( append ~= "" ) then | if ( append ~= "" ) then r = append .. "<br />" .. r | ||
end else error( r, 0 ) end end | |||
end | return rend -- TemplatePar.assert()TemplatePar.check = function ( options ) | ||
return | |||
TemplatePar.check = function ( options ) | |||
-- Run parameter analysis on current template environment | -- Run parameter analysis on current template environment | ||
-- Precondition: | -- Precondition: -- options -- table or nil; optional details | ||
-- options.mandatory -- options.optional | |||
-- options.mandatory | -- Postcondition: -- Return string with error message as configured; | ||
-- false if valid -- Uses: -- form() | |||
-- Postcondition: | return form( true, options )end -- TemplatePar.check() | ||
TemplatePar.count = function () -- Return number of template parameters | |||
-- false if valid | -- Postcondition: -- Return number, starting at 0 -- Uses: | ||
-- mw.getCurrentFrame() -- frame:getParent() local k, v | |||
local r = 0 local t = mw.getCurrentFrame():getParent() | |||
return form( true, options ) | local o = t.args for k, v in pairs( o ) do r = r + 1 | ||
end -- TemplatePar.check() | end -- for k, v return rend -- TemplatePar.count() | ||
TemplatePar.count = function () | |||
-- Postcondition: | |||
-- mw.getCurrentFrame() | |||
local r = 0 | |||
local o = t.args | |||
end -- for k, v | |||
TemplatePar.countNotEmpty = function () | TemplatePar.countNotEmpty = function () | ||
-- Return number of template parameters with more than whitespace | -- Return number of template parameters with more than whitespace | ||
-- Postcondition: | -- Postcondition: -- Return number, starting at 0 -- Uses: | ||
-- mw.getCurrentFrame() -- frame:getParent() local k, v | |||
local r = 0 local t = mw.getCurrentFrame():getParent() | |||
-- mw.getCurrentFrame() | local o = t.args for k, v in pairs( o ) do | ||
if not v:match( "^%s*$" ) then r = r + 1 end | |||
end -- for k, v return rend -- TemplatePar.countNotEmpty() | |||
local r = 0 | |||
local o = t.args | |||
if not v:match( "^%s*$" ) then | |||
end -- for k, v | |||
TemplatePar.downcase = function ( options ) | TemplatePar.downcase = function ( options ) | ||
-- Return all template parameters with downcased name | -- Return all template parameters with downcased name -- Precondition: | ||
-- options -- table or nil; optional messaging details | -- options -- table or nil; optional messaging details | ||
-- Postcondition: | -- Postcondition: | ||
-- Return table, may be empty; or string with error message. | -- Return table, may be empty; or string with error message. -- Uses: | ||
-- mw.getCurrentFrame() -- frame:getParent() -- flat() | |||
-- mw.getCurrentFrame() | local t = mw.getCurrentFrame():getParent() return flat( t.args, options ) | ||
end -- TemplatePar.downcase()TemplatePar.valid = function ( access, options ) | |||
-- Check validity of one particular template parameter -- Precondition: | |||
local t = mw.getCurrentFrame():getParent() | |||
end -- TemplatePar.downcase() | |||
TemplatePar.valid = function ( access, options ) | |||
-- Check validity of one particular template parameter | |||
-- access -- id of parameter in template transclusion | -- access -- id of parameter in template transclusion | ||
-- options -- table or nil; optional details | -- options -- table or nil; optional details -- Postcondition: | ||
-- Return string with error message as configured; | -- Return string with error message as configured; | ||
-- false if valid or no answer permitted | -- false if valid or no answer permitted -- Uses: | ||
-- mw.text.trim() -- TemplatePar.downcase() | |||
-- mw.text.trim() | -- mw.getCurrentFrame() -- frame:getParent() | ||
-- formatted() -- failure() -- finalize() local r | |||
-- mw.getCurrentFrame() | if type( access ) == "string" then r = mw.text.trim( access ) | ||
if #r == 0 then r = false end end if r then | |||
-- formatted() | local params if type( options ) ~= "table" then | ||
options = { } end if options.low then | |||
params = TemplatePar.downcase( options ) else | |||
params = mw.getCurrentFrame():getParent() end | |||
if type( access ) == "string" then | r = formatted( params, access, options ) else | ||
r = failure( "noname", false, options ) end | |||
if #r == 0 then | return finalize( r, options )end -- TemplatePar.valid() | ||
local params | |||
options = { } | |||
params = TemplatePar.downcase( options ) | |||
params = mw.getCurrentFrame():getParent() | |||
r = formatted( params, access, options ) | |||
r = failure( "noname", false, options ) | |||
return finalize( r, options ) | |||
end -- TemplatePar.valid() | |||
TemplatePar.verify = function ( options ) | TemplatePar.verify = function ( options ) | ||
-- Perform #invoke parameter analysis | -- Perform #invoke parameter analysis -- Precondition: | ||
-- options -- table or nil; optional details -- Postcondition: | |||
-- options -- table or nil; optional details | |||
-- Return string with error message as configured; | -- Return string with error message as configured; | ||
-- false if valid | -- false if valid -- Uses: -- form() | ||
return form( false, options )end -- TemplatePar.verify() | |||
-- Provide external accesslocal p = {}function p.assert( frame ) | |||
return form( false, options ) | -- Perform parameter analysis on some single string -- Precondition: | ||
end -- TemplatePar.verify() | -- frame -- object; #invoke environment -- Postcondition: | ||
-- Return string with error message or "" -- Uses: | |||
-- furnish() return furnish( frame, "assert" )end -- .assert() | |||
function p.check( frame ) -- Check validity of template parameters | |||
-- Provide external | -- Precondition: -- frame -- object; #invoke environment | ||
-- Postcondition: -- Return string with error message or "" | |||
-- Uses: -- form() -- fill() | |||
function p.assert( frame ) | |||
-- Perform parameter analysis on some single string | |||
-- frame -- object; #invoke environment | |||
-- Return string with error message or "" | |||
-- furnish() | |||
end -- .assert() | |||
function p.check( frame ) | |||
-- Precondition: | |||
-- Postcondition: | |||
-- Uses: | |||
local options = { optional = { "all", | local options = { optional = { "all", | ||
"opt", | "opt", | ||
| Line 1,045: | Line 481: | ||
"template" }, | "template" }, | ||
template = "#invoke:TemplatePar|check|" | template = "#invoke:TemplatePar|check|" | ||
} | } local r = form( false, options ) if not r then | ||
options = { mandatory = fill( frame.args.all ), | options = { mandatory = fill( frame.args.all ), | ||
optional = fill( frame.args.opt ), | optional = fill( frame.args.opt ), | ||
| Line 1,053: | Line 487: | ||
low = frame.args.low, | low = frame.args.low, | ||
noError = frame.args.noError, | noError = frame.args.noError, | ||
template = frame.args.template | template = frame.args.template } | ||
r = form( true, options ) end return r or ""end -- .check() | |||
r = form( true, options ) | function p.count( frame ) -- Count number of template parameters | ||
-- Postcondition: -- Return string with digits including "0" | |||
-- Uses: -- TemplatePar.count() | |||
end -- .check() | return tostring( TemplatePar.count() )end -- .count() | ||
function p.count( frame ) | |||
-- Postcondition: | |||
-- Uses: | |||
return tostring( TemplatePar.count() ) | |||
end -- .count() | |||
function p.countNotEmpty( frame ) | function p.countNotEmpty( frame ) | ||
-- Count number of template parameters which are not empty | -- Count number of template parameters which are not empty | ||
-- Postcondition: | -- Postcondition: -- Return string with digits including "0" | ||
-- Uses: -- TemplatePar.countNotEmpty() | |||
-- Uses: | return tostring( TemplatePar.countNotEmpty() )end -- .countNotEmpty() | ||
function p.match( frame ) -- Combined analysis of parameters and their values | |||
return tostring( TemplatePar.countNotEmpty() ) | -- Postcondition: -- Return string with error message or "" | ||
end -- .countNotEmpty() | -- Uses: -- mw.text.trim() -- mw.ustring.lower() | ||
-- failure() -- form() -- TemplatePar.downcase() | |||
-- figure() -- feasible() -- fault() -- finalize() | |||
local r = false local options = { cat = frame.args.cat, | |||
function p.match( frame ) | |||
-- Postcondition: | |||
-- Uses: | |||
-- failure() | |||
-- figure() | |||
local r = false | |||
low = frame.args.low, | low = frame.args.low, | ||
noError = frame.args.noError, | noError = frame.args.noError, | ||
template = frame.args.template | template = frame.args.template } | ||
local k, v, s local params = { } for k, v in pairs( frame.args ) do | |||
local k, v, s | |||
if type( k ) == "number" then | if type( k ) == "number" then | ||
s, v = v:match( "^ *([^=]+) *= *(%S.*%S*) *$" ) | s, v = v:match( "^ *([^=]+) *= *(%S.*%S*) *$" ) if s then | ||
s = mw.text.trim( s ) if s == "" then | |||
s = mw.text.trim( s ) | s = false end end | ||
if s then if options.low then | |||
s = false | s = mw.ustring.lower( s ) end | ||
if params[ s ] then s = params[ s ] | |||
s[ #s + 1 ] = v else | |||
if s then | params[ s ] = { v } end else | ||
s = mw.ustring.lower( s ) | |||
if params[ s ] then | |||
s[ #s + 1 ] = v | |||
params[ s ] = { v } | |||
r = failure( "invalidPar", tostring( k ), options ) | r = failure( "invalidPar", tostring( k ), options ) | ||
break -- for k, v | break -- for k, v end end end -- for k, v | ||
if not r then s = { } for k, v in pairs( params ) do | |||
s[ #s + 1 ] = k end -- for k, v options.optional = s | |||
r = form( true, options ) end if not r then | |||
if not r then | |||
s[ #s + 1 ] = k | |||
r = form( true, options ) | |||
local errMiss, errValues, lack, rule | local errMiss, errValues, lack, rule | ||
local targs = frame:getParent().args | local targs = frame:getParent().args options.optional = nil | ||
if options.low then targs = TemplatePar.downcase() | |||
if options.low then | else targs = frame:getParent().args end | ||
errMiss = false errValues = false | |||
else | for k, v in pairs( params ) do options.say = k | ||
errValue = false s = targs[ k ] if s then | |||
if s == "" then lack = true | |||
errMiss = false | else lack = false end | ||
else s = "" lack = true | |||
for k, v in pairs( params ) do | end for r, rule in pairs( v ) do | ||
errValue = false | |||
if s == "" then | |||
else | |||
else | |||
end | |||
options = figure( rule, options ) | options = figure( rule, options ) | ||
r = feasible( s, options, true ) | r = feasible( s, options, true ) if r then | ||
if lack then if errMiss then | |||
if lack then | |||
errMiss = errMiss .. ", '" .. k .. "'" | errMiss = errMiss .. ", '" .. k .. "'" | ||
else | else | ||
errMiss = "'" .. k .. "'" | errMiss = "'" .. k .. "'" end | ||
elseif not errMiss then | elseif not errMiss then | ||
errValues = fault( errValues, r ) | errValues = fault( errValues, r ) end | ||
break -- for r, rule end | |||
break -- for r, rule | end -- for s, rule end -- for k, v | ||
r = ( errMiss or errValues ) if r then if errMiss then | |||
end -- for s, rule | r = failure( "undefined", errMiss, options ) else | ||
r = failure( "invalid", errValues, options ) end | |||
r = ( errMiss or errValues ) | r = finalize( r, options ) end end return r or "" | ||
end -- .match()function p.valid( frame ) | |||
-- Check validity of one particular template parameter -- Precondition: | |||
r = failure( "undefined", errMiss, options ) | -- frame -- object; #invoke environment -- Postcondition: | ||
-- Return string with error message or "" -- Uses: | |||
r = failure( "invalid", errValues, options ) | -- furnish() return furnish( frame, "valid" )end -- .valid() | ||
function p.TemplatePar() -- Retrieve function access for modules | |||
r = finalize( r, options ) | -- Postcondition: -- Return table with functions | ||
return TemplateParend -- .TemplatePar()return p | |||
end -- .match() | |||
function p.valid( frame ) | |||
-- Check validity of one particular template parameter | |||
-- frame -- object; #invoke environment | |||
-- Return string with error message or "" | |||
-- furnish() | |||
end -- .valid() | |||
function p.TemplatePar() | |||
-- Postcondition: | |||
return | |||
return p | |||
Revision as of 08:40, 20 December 2013
This documentation is transcluded from Module:TemplatePar/doc.
Script error: Lua error at line 68: '<eof>' expected near 'end'.
--[=[ TemplatePar 2013-07-09Template parameter utility* assert* check* count
* countNotEmpty* downcase* match* valid* verify* TemplatePar()]=]
-- Module globalslocal TemplatePar = { }
local messagePrefix = "lua-module-TemplatePar-"local l10nDef = {}
l10nDef[ "en" ] = {
badPattern = "#invoke:TemplatePar pattern syntax error",
dupOpt = "#invoke:TemplatePar repeated optional parameter",
dupRule = "#invoke:TemplatePar conflict key/pattern",
empty = "Error in template * undefined value for mandatory",
invalid = "Error in template * invalid parameter",
invalidPar = "#invoke:TemplatePar invalid parameter",
minmax = "#invoke:TemplatePar min > max",
multiSpell = "Error in template * multiple spelling of parameter",
noErrorCat = "#invoke:TemplatePar noError and missing category",
noname = "#invoke:TemplatePar missing parameter name",
tooLong = "Error in template * parameter too long",
tooShort = "Error in template * parameter too short",
undefined = "Error in template * mandatory parameter missing",
unknown = "Error in template * unknown parameter name",
unknownRule = "#invoke:TemplatePar unknown rule"}l10nDef[ "de" ] = {
badPattern = "#invoke:TemplatePar Syntaxfehler des pattern",
dupOpt = "#invoke:TemplatePar Optionsparameter wiederholt",
dupRule = "#invoke:TemplatePar Konflikt key/pattern",
empty = "Fehler bei Vorlage * Pflichtparameter ohne Wert",
invalid = "Fehler bei Vorlage * Parameter ungültig",
invalidPar = "#invoke:TemplatePar Ungültiger Parameter",
minmax = "#invoke:TemplatePar min > max",
multiSpell = "Fehler bei Vorlage * Mehrere Parameter-Schreibweisen",
noErrorCat = "#invoke:TemplatePar noError und keine Kategorie",
noname = "#invoke:TemplatePar Parameter nicht angegeben",
tooLong = "Fehler bei Vorlage * Parameter zu lang",
tooShort = "Fehler bei Vorlage * Parameter zu kurz",
undefined = "Fehler bei Vorlage * Pflichtparameter fehlt",
unknown = "Fehler bei Vorlage * Parametername unbekannt",
unknownRule = "#invoke:TemplatePar Unbekannte Regel"}local Patterns = {
[ "ASCII" ] = "^[ -~]*$", [ "ASCII+" ] = "^[ -~]+$",
[ "ASCII+1" ] = "^[!-~]+$", [ "n" ] = "^%-?[0-9]*$",
[ "n>0" ] = "^[0-9]*[1-9][0-9]*$",
[ "N+" ] = "^%-?[1-9][0-9]*$", [ "N>0" ] = "^[1-9][0-9]*$",
[ "x" ] = "^[0-9A-Fa-f]*$", [ "x+" ] = "^[0-9A-Fa-f]+$",
[ "X" ] = "^[0-9A-F]*$", [ "X+" ] = "^[0-9A-F]+$",
[ "0,0" ] = "^%-?[0-9]*,?[0-9]*$",
[ "0,0+" ] = "^%-?[0-9]+,[0-9]+$",
[ "0,0+?" ] = "^%-?[0-9]+,?[0-9]*$",
[ "0.0" ] = "^%-?[0-9]*%.?[0-9]*$",
[ "0.0+" ] = "^%-?[0-9]+%.[0-9]+$",
[ "0.0+?" ] = "^%-?[0-9]+%.?[0-9]*$",
[ ".0+" ] = "^%-?[0-9]*%.?[0-9]+$",
[ "ID" ] = "^[A-Za-z]?[A-Za-z_0-9]*$",
[ "ID+" ] = "^[A-Za-z][A-Za-z_0-9]*$", [ "ABC" ] = "^[A-Z]*$",
[ "ABC+" ] = "^[A-Z]+$", [ "Abc" ] = "^[A-Z]*[a-z]*$",
[ "Abc+" ] = "^[A-Z][a-z]+$", [ "abc" ] = "^[a-z]*$",
[ "abc+" ] = "^[a-z]+$", [ "aBc+" ] = "^[a-z]+[A-Z][A-Za-z]*$",
[ "base64" ] = "^[A-Za-z0-9%+/]*$",
[ "base64+" ] = "^[A-Za-z0-9%+/]+$", [ "aa" ] = "[%a%a].*[%a%a]",
[ "pagename" ] = string.format( "^[^#<>%%[%%]|{}%s%-s%s]+$",
1, 31, 127 ), [ "+" ] = "%S"}
local patternCJK = falselocal function containsCJK( s )
-- Is any CJK character present? -- Precondition: -- s -- string
-- Postcondition: -- Return false iff no CJK present -- Uses:
-- >< patternCJK -- mw.ustring.char()
-- mw.ustring.match() local r = false if not patternCJK then
patternCJK = mw.ustring.char( 91,
13312, 45, 40959,
131072, 45, 178207,
93 ) end
if mw.ustring.match( s, patternCJK ) then r = true end return r
end -- containsCJK()local function factory( say )
-- Retrieve localized message string in content language -- Precondition:
-- say -- string; message ID -- Postcondition:
-- Return some message string -- Uses: -- > messagePrefix
-- > l10nDef -- mw.language.getContentLanguage()
-- mw.message.new()
local c = mw.language.getContentLanguage():getCode()
local m = mw.message.new( messagePrefix .. say ) local r = false
if m:isBlank() then local l10n = l10nDef[ c ] if not l10n then
l10n = l10nDef[ "en" ] end r = l10n[ say ] else
m:inLanguage( c ) r = m:plain() end if not r then
r = "(((".. say .. ")))" end return rend -- factory()
local function failsafe( story, scan )
-- Test for match (possibly user-defined with syntax error)
-- Precondition: -- story -- string; parameter value
-- scan -- string; pattern -- Postcondition:
-- Return nil, if not matching, else non-nil -- Uses:
-- mw.ustring.match() return mw.ustring.match( story, scan )
end -- failsafe()local function failure( spec, suspect, options )
-- Submit localized error message -- Precondition:
-- spec -- string; message ID
-- suspect -- string or nil; additional information
-- options -- table or nil; optional details
-- options.template -- Postcondition:
-- Return string -- Uses: -- factory()
local r = factory( spec ) if type( options ) == "table" then
if type( options.template ) == "string" then
if #options.template > 0 then
r = r .. " (" .. options.template .. ")" end
end end if suspect then r = r .. ": " .. suspect end
return rend -- failure()local function fault( store, key )
-- Add key to collection string and insert separator -- Precondition:
-- store -- string or nil or false; collection string
-- key -- string or number; to be appended -- Postcondition:
-- Return string; extended local r local s
if type( key ) == "number" then s = tostring( key ) else
s = key end if store then r = store .. "; " .. s else
r = s end return rend -- fault()
local function feasible( analyze, options, abbr ) -- Check content of a value
-- Precondition: -- analyze -- string to be analyzed
-- options -- table or nil; optional details
-- options.pattern -- options.key
-- options.say
-- abbr -- true: abbreviated error message -- Postcondition:
-- Return string with error message as configured;
-- false if valid or no answer permitted -- Uses:
-- > Patterns -- failure() -- mw.text.trim()
-- failsafe() -- containsCJK() local r = false
local s = false local show = nil local scan = false
if type( options.pattern ) == "string" then if options.key then
r = failure( "dupRule", false, options ) else
scan = options.pattern end else
if type( options.key ) == "string" then
s = mw.text.trim( options.key ) else s = "+"
end if s ~= "*" then scan = Patterns[ s ] end
if type( scan ) == "string" then
if s == "n" or s == "0,0" or s == "0.0" then
if not analyze:match( "[0-9]" ) then
scan = false if options.say then
show = "'" .. options.say .. "'" end
if abbr then r = show
else
r = failure( "invalid", show, options )
end end end
elseif s ~= "*" then
local op, n, plus = s:match( "([<!>]=?)([-0-9][%S]*)(+?)" )
if op then n = tonumber( n ) if n then
local i = tonumber( analyze ) if i then
if op == "<" then
i = ( i < n )
elseif op == "<=" then
i = ( i <= n )
elseif op == ">" then
i = ( i > n )
elseif op == ">=" then
i = ( i >= n )
elseif op == "!=" then
i = ( i ~= n ) else
n = false end
end if not i then
r = "invalid" end
elseif plus then r = "undefined"
end end if not n and not r then
r = "unknownRule" end if r then
if options.say then
show = "'" .. options.say .. "' " .. s else
show = s end if abbr then
r = show else
r = failure( r, show, options ) end
end end end if scan then
local legal, got = pcall( failsafe, analyze, scan ) if legal then
if not got then if s == "aa" then
got = containsCJK( analyze ) end
if not got then if options.say then
show = "'" .. options.say .. "'" end
if abbr then r = show
else
r = failure( "invalid", show, options )
end end end else
r = failure( "badPattern",
scan .. " *** " .. got,
options ) end end return r
end -- feasible()local function fed( haystack, needle )
-- Find needle in haystack map -- Precondition:
-- haystack -- table; map of key values
-- needle -- any; identifier -- Postcondition:
-- Return true iff found local k, v
for k, v in pairs( haystack ) do if k == needle then
return true end end -- for k, v return false
end -- fed()local function fetch( light, options )
-- Return regular table with all parameters -- Precondition:
-- light -- true: template transclusion; false: #invoke
-- options -- table; optional details -- options.low
-- Postcondition: -- Return table; whitespace-only values as false
-- Uses: -- TemplatePar.downcase() -- mw.getCurrentFrame()
-- frame:getParent() local g, k, v local r = { }
if options.low then g = TemplatePar.downcase( options ) else
g = mw.getCurrentFrame() if light then
g = g:getParent() end g = g.args end
if type( g ) == "table" then r = { }
for k, v in pairs( g ) do if type( v ) == "string" then
if v:match( "^%s*$" ) then v = false
end else v = false end
if type( k ) == "number" then k = tostring( k )
end r[ k ] = v end -- for k, v else
r = g end return rend -- fetch()
local function figure( append, options )
-- Extend options by rule from #invoke strings -- Precondition:
-- append -- string or nil; requested rule
-- options -- table; details -- ++ .key
-- ++ .pattern -- Postcondition:
-- Return sequence table local r = options
if type( append ) == "string" then
local story = mw.text.trim( append )
local sub = story:match( "^/(.*%S)/$" )
if type( sub ) == "string" then
sub = sub:gsub( "%%!", "|" )
sub = sub:gsub( "%%%(%(", "{{" )
sub = sub:gsub( "%%%)%)", "}}" )
options.pattern = sub options.key = nil else
options.key = story options.pattern = nil end
end return rend -- figure()local function fill( specified )
-- Split requirement string separated by '=' -- Precondition:
-- specified -- string or nil; requested parameter set
-- Postcondition: -- Return sequence table -- Uses:
-- mw.text.split() local r if specified then local i, s
r = mw.text.split( specified, "%s*=%s*" ) for i = #r, 1, -1 do
s = r[ i ] if #s == 0 then
table.remove( r, i ) end end -- for i, -1
else r = { } end return rend -- fill()
local function finalize( submit, options ) -- Finalize message
-- Precondition:
-- submit -- string or false or nil; non-empty error message
-- options -- table or nil; optional details
-- options.noError -- options.cat
-- options.template -- Postcondition:
-- Return string or false -- Uses: -- factory()
local r = false if submit then local opt, s
if type( options ) == "table" then opt = options else
opt = { } end if opt.noError then
if not opt.cat then
r = submit .. " " .. factory( "noErrorCat" ) end
else r = submit end if r then
r = "<span class='error'>" .. r .. "</span>" end
s = opt.cat if type( s ) == "string" then
if not r then r = "" end
if s:find( "@@@" ) then
if type( opt.template ) == "string" then
s = s:gsub( "@@@", opt.template ) end
end r = r .. "[[Category:" .. s .. "]]" end end
return rend -- finalize()local function finder( haystack, needle )
-- Find needle in haystack sequence -- Precondition:
-- haystack -- table; sequence of key names, downcased if low
-- needle -- any; key name -- Postcondition:
-- Return true iff found local i for i = 1, #haystack do
if haystack[ i ] == needle then return true end
end -- for i return falseend -- finder()
local function fix( valid, duty, got, options ) -- Perform parameter analysis
-- Precondition:
-- valid -- table; unique sequence of known parameters
-- duty -- table; sequence of mandatory parameters
-- got -- table; sequence of current parameters
-- options -- table or nil; optional details -- Postcondition:
-- Return string as configured; empty if valid -- Uses:
-- finder() -- fault() -- failure() -- fed()
local k, v local r = false for k, v in pairs( got ) do
if not finder( valid, k ) then r = fault( r, k ) end
end -- for k, v if r then
r = failure( "unknown", "'" .. r .. "'", options )
else -- all names valid local i, s for i = 1, #duty do
s = duty[ i ] if not fed( got, s ) then
r = fault( r, s ) end end -- for i
if r then r = failure( "undefined", r, options )
else -- all mandatory present for i = 1, #duty do
s = duty[ i ] if not got[ s ] then
r = fault( r, s ) end end -- for i
if r then r = failure( "empty", r, options )
end end end return rend -- fix()
local function flat( collection, options )
-- Return all table elements with downcased string -- Precondition:
-- collection -- table; k=v pairs
-- options -- table or nil; optional messaging details
-- Postcondition:
-- Return table, may be empty; or string with error message. -- Uses:
-- mw.ustring.lower() -- fault() -- failure()
local k, v local r = { } local e = false
for k, v in pairs( collection ) do if type ( k ) == "string" then
k = mw.ustring.lower( k ) if r[ k ] then
e = fault( e, k ) end end r[ k ] = v
end -- for k, v if e then r = failure( "multiSpell", e, options )
end return rend -- flat()local function fold( options )
-- Merge two tables, create new sequence if both not empty
-- Precondition: -- options -- table; details
-- options.mandatory sequence to keep unchanged
-- options.optional sequence to be appended
-- options.low downcased expected
-- Postcondition: -- Return merged table, or message string if error
-- Uses: -- finder() -- fault() -- failure()
-- flat() local i, e, r, s local base = options.mandatory
local extend = options.optional if #base == 0 then
if #extend == 0 then r = { } else
r = extend end else if #extend == 0 then
r = base else e = false
for i = 1, #extend do s = extend[ i ]
if finder( base, s ) then e = fault( e, s )
end end -- for i if e then
r = failure( "dupOpt", e, options ) else
r = { } for i = 1, #base do
table.insert( r, base[ i ] ) end -- for i
for i = 1, #extend do
table.insert( r, extend[ i ] ) end -- for i
end end end
if options.low and type( r ) == "table" then r = flat( r, options )
end return rend -- fold()local function form( light, options )
-- Run parameter analysis on current environment -- Precondition:
-- light -- true: template transclusion; false: #invoke
-- options -- table or nil; optional details
-- options.mandatory -- options.optional
-- Postcondition: -- Return string with error message as configured;
-- false if valid -- Uses: -- fold() -- fetch()
-- fix() -- finalize() local duty, r
if type( options ) == "table" then
if type( options.mandatory ) ~= "table" then
options.mandatory = { } end duty = options.mandatory
if type( options.optional ) ~= "table" then
options.optional = { } end r = fold( options ) else
options = { } duty = { } r = { } end
if type( r ) == "table" then local got = fetch( light, options )
if type( got ) == "table" then
r = fix( r, duty, got, options ) else r = got
end end return finalize( r, options )end -- form()
local function format( analyze, options ) -- Check validity of a value
-- Precondition: -- analyze -- string to be analyzed
-- options -- table or nil; optional details
-- options.say -- options.min
-- options.max -- Postcondition:
-- Return string with error message as configured;
-- false if valid or no answer permitted -- Uses:
-- feasible() -- failure()
local r = feasible( analyze, options, false ) local show
if options.min and not r then
if type( options.min ) == "number" then
if type( options.max ) == "number" then
if options.max < options.min then
r = failure( "minmax",
tostring( options.min )
.. " > " ..
tostring( options.max ),
options ) end end
if #analyze < options.min and not r then
show = " <" .. options.min if options.say then
show = show .. " '" .. options.say .. "'" end
r = failure( "tooShort", show, options ) end
else r = failure( "invalidPar", "min", options ) end
end if options.max and not r then
if type( options.max ) == "number" then
if #analyze > options.max then
show = " >" .. options.max if options.say then
show = show .. " '" .. options.say .. "'" end
r = failure( "tooLong", show, options ) end
else r = failure( "invalidPar", "max", options ) end
end return rend -- format()
local function formatted( assignment, access, options )
-- Check validity of one particular parameter in a collection
-- Precondition: -- assignment -- collection
-- access -- id of parameter in collection
-- options -- table or nil; optional details -- Postcondition:
-- Return string with error message as configured;
-- false if valid or no answer permitted -- Uses:
-- format() -- failure() local r = false
if type( assignment ) == "table" then
local story = assignment.args[ access ]
if type( story ) == "string" then
if type( options ) ~= "table" then options = { }
end options.say = access
r = format( story, options ) else
r = failure( "invalid", access, options ) end end
return rend -- formatted()local function furnish( frame, action )
-- Prepare #invoke evaluation of .assert() or .valid() -- Precondition:
-- frame -- object; #invoke environment
-- action -- "assert" or "valid" -- Postcondition:
-- Return string with error message or "" -- Uses: -- form()
-- mw.text.trim() -- failure() -- TemplatePar.assert()
-- TemplatePar.valid() local options = { mandatory = { "1" },
optional = { "2",
"cat",
"low",
"max",
"min",
"noError",
"template" },
template = "#invoke:TemplatePar|".. action .. "|"
} local r = form( false, options ) if not r then
local s options = { cat = frame.args.cat,
low = frame.args.low,
noError = frame.args.noError,
template = frame.args.template }
options = figure( frame.args[ 2 ], options )
if type( frame.args.min ) == "string" then
s = frame.args.min:match( "^%s*([0-9]+)%s*$" ) if s then
options.min = tonumber( s ) else
r = failure( "invalidPar",
"min=" .. frame.args.min,
options ) end end
if type( frame.args.max ) == "string" then
s = frame.args.max:match( "^%s*([1-9][0-9]*)%s*$" )
if s then options.max = tonumber( s ) else
r = failure( "invalidPar",
"max=" .. frame.args.max,
options ) end end
if r then r = finalize( r, options ) else
s = frame.args[ 1 ] or "" r = tonumber( s )
if ( r ) then s = r end
r = TemplatePar[ action ]( s, options ) end end
return r or ""end -- furnish()
TemplatePar.assert = function ( analyze, append, options )
-- Perform parameter analysis on a single string -- Precondition:
-- analyze -- string to be analyzed
-- append -- string: append error message, prepending <br />
-- false or nil: throw error with message
-- options -- table; optional details -- Postcondition:
-- Return string with error message as configured;
-- false if valid -- Uses: -- format()
local r = format( analyze, options ) if ( r ) then
if ( type( append ) == "string" ) then
if ( append ~= "" ) then r = append .. "<br />" .. r
end else error( r, 0 ) end end
return rend -- TemplatePar.assert()TemplatePar.check = function ( options )
-- Run parameter analysis on current template environment
-- Precondition: -- options -- table or nil; optional details
-- options.mandatory -- options.optional
-- Postcondition: -- Return string with error message as configured;
-- false if valid -- Uses: -- form()
return form( true, options )end -- TemplatePar.check()
TemplatePar.count = function () -- Return number of template parameters
-- Postcondition: -- Return number, starting at 0 -- Uses:
-- mw.getCurrentFrame() -- frame:getParent() local k, v
local r = 0 local t = mw.getCurrentFrame():getParent()
local o = t.args for k, v in pairs( o ) do r = r + 1
end -- for k, v return rend -- TemplatePar.count()
TemplatePar.countNotEmpty = function ()
-- Return number of template parameters with more than whitespace
-- Postcondition: -- Return number, starting at 0 -- Uses:
-- mw.getCurrentFrame() -- frame:getParent() local k, v
local r = 0 local t = mw.getCurrentFrame():getParent()
local o = t.args for k, v in pairs( o ) do
if not v:match( "^%s*$" ) then r = r + 1 end
end -- for k, v return rend -- TemplatePar.countNotEmpty()
TemplatePar.downcase = function ( options )
-- Return all template parameters with downcased name -- Precondition:
-- options -- table or nil; optional messaging details
-- Postcondition:
-- Return table, may be empty; or string with error message. -- Uses:
-- mw.getCurrentFrame() -- frame:getParent() -- flat()
local t = mw.getCurrentFrame():getParent() return flat( t.args, options )
end -- TemplatePar.downcase()TemplatePar.valid = function ( access, options )
-- Check validity of one particular template parameter -- Precondition:
-- access -- id of parameter in template transclusion
-- options -- table or nil; optional details -- Postcondition:
-- Return string with error message as configured;
-- false if valid or no answer permitted -- Uses:
-- mw.text.trim() -- TemplatePar.downcase()
-- mw.getCurrentFrame() -- frame:getParent()
-- formatted() -- failure() -- finalize() local r
if type( access ) == "string" then r = mw.text.trim( access )
if #r == 0 then r = false end end if r then
local params if type( options ) ~= "table" then
options = { } end if options.low then
params = TemplatePar.downcase( options ) else
params = mw.getCurrentFrame():getParent() end
r = formatted( params, access, options ) else
r = failure( "noname", false, options ) end
return finalize( r, options )end -- TemplatePar.valid()
TemplatePar.verify = function ( options )
-- Perform #invoke parameter analysis -- Precondition:
-- options -- table or nil; optional details -- Postcondition:
-- Return string with error message as configured;
-- false if valid -- Uses: -- form()
return form( false, options )end -- TemplatePar.verify()
-- Provide external accesslocal p = {}function p.assert( frame )
-- Perform parameter analysis on some single string -- Precondition:
-- frame -- object; #invoke environment -- Postcondition:
-- Return string with error message or "" -- Uses:
-- furnish() return furnish( frame, "assert" )end -- .assert()
function p.check( frame ) -- Check validity of template parameters
-- Precondition: -- frame -- object; #invoke environment
-- Postcondition: -- Return string with error message or ""
-- Uses: -- form() -- fill()
local options = { optional = { "all",
"opt",
"cat",
"low",
"noError",
"template" },
template = "#invoke:TemplatePar|check|"
} local r = form( false, options ) if not r then
options = { mandatory = fill( frame.args.all ),
optional = fill( frame.args.opt ),
cat = frame.args.cat,
low = frame.args.low,
noError = frame.args.noError,
template = frame.args.template }
r = form( true, options ) end return r or ""end -- .check()
function p.count( frame ) -- Count number of template parameters
-- Postcondition: -- Return string with digits including "0"
-- Uses: -- TemplatePar.count()
return tostring( TemplatePar.count() )end -- .count()
function p.countNotEmpty( frame )
-- Count number of template parameters which are not empty
-- Postcondition: -- Return string with digits including "0"
-- Uses: -- TemplatePar.countNotEmpty()
return tostring( TemplatePar.countNotEmpty() )end -- .countNotEmpty()
function p.match( frame ) -- Combined analysis of parameters and their values
-- Postcondition: -- Return string with error message or ""
-- Uses: -- mw.text.trim() -- mw.ustring.lower()
-- failure() -- form() -- TemplatePar.downcase()
-- figure() -- feasible() -- fault() -- finalize()
local r = false local options = { cat = frame.args.cat,
low = frame.args.low,
noError = frame.args.noError,
template = frame.args.template }
local k, v, s local params = { } for k, v in pairs( frame.args ) do
if type( k ) == "number" then
s, v = v:match( "^ *([^=]+) *= *(%S.*%S*) *$" ) if s then
s = mw.text.trim( s ) if s == "" then
s = false end end
if s then if options.low then
s = mw.ustring.lower( s ) end
if params[ s ] then s = params[ s ]
s[ #s + 1 ] = v else
params[ s ] = { v } end else
r = failure( "invalidPar", tostring( k ), options )
break -- for k, v end end end -- for k, v
if not r then s = { } for k, v in pairs( params ) do
s[ #s + 1 ] = k end -- for k, v options.optional = s
r = form( true, options ) end if not r then
local errMiss, errValues, lack, rule
local targs = frame:getParent().args options.optional = nil
if options.low then targs = TemplatePar.downcase()
else targs = frame:getParent().args end
errMiss = false errValues = false
for k, v in pairs( params ) do options.say = k
errValue = false s = targs[ k ] if s then
if s == "" then lack = true
else lack = false end
else s = "" lack = true
end for r, rule in pairs( v ) do
options = figure( rule, options )
r = feasible( s, options, true ) if r then
if lack then if errMiss then
errMiss = errMiss .. ", '" .. k .. "'"
else
errMiss = "'" .. k .. "'" end
elseif not errMiss then
errValues = fault( errValues, r ) end
break -- for r, rule end
end -- for s, rule end -- for k, v
r = ( errMiss or errValues ) if r then if errMiss then
r = failure( "undefined", errMiss, options ) else
r = failure( "invalid", errValues, options ) end
r = finalize( r, options ) end end return r or ""
end -- .match()function p.valid( frame )
-- Check validity of one particular template parameter -- Precondition:
-- frame -- object; #invoke environment -- Postcondition:
-- Return string with error message or "" -- Uses:
-- furnish() return furnish( frame, "valid" )end -- .valid()
function p.TemplatePar() -- Retrieve function access for modules
-- Postcondition: -- Return table with functions
return TemplateParend -- .TemplatePar()return p