Modul:TemplatePar: Unterschied zwischen den Versionen
updates + match
wp>PerfektesChaos (+pagename) |
wp>PerfektesChaos (updates + match) |
||
Zeile 1: | Zeile 1: | ||
--[=[ TemplatePar 2013- | --[=[ TemplatePar 2013-07-09 | ||
Template parameter utility | Template parameter utility | ||
* assert | * assert | ||
Zeile 6: | Zeile 6: | ||
* countNotEmpty | * countNotEmpty | ||
* downcase | * downcase | ||
* match | |||
* valid | * valid | ||
* verify | * verify | ||
Zeile 18: | Zeile 19: | ||
local l10nDef = {} | local l10nDef = {} | ||
l10nDef[ "en" ] = { | l10nDef[ "en" ] = { | ||
badPattern = "#invoke:TemplatePar | badPattern = "#invoke:TemplatePar pattern syntax error", | ||
dupOpt = "#invoke:TemplatePar | dupOpt = "#invoke:TemplatePar repeated optional parameter", | ||
dupRule = "#invoke:TemplatePar | dupRule = "#invoke:TemplatePar conflict key/pattern", | ||
empty = "Error in template * undefined value for mandatory", | empty = "Error in template * undefined value for mandatory", | ||
invalid = "Error in template * invalid parameter", | invalid = "Error in template * invalid parameter", | ||
invalidPar = "#invoke:TemplatePar | invalidPar = "#invoke:TemplatePar invalid parameter", | ||
minmax = "#invoke:TemplatePar | minmax = "#invoke:TemplatePar min > max", | ||
multiSpell = "Error in template * multiple spelling of parameter", | multiSpell = "Error in template * multiple spelling of parameter", | ||
noErrorCat = "#invoke:TemplatePar | noErrorCat = "#invoke:TemplatePar noError and missing category", | ||
noname = "#invoke:TemplatePar | noname = "#invoke:TemplatePar missing parameter name", | ||
tooLong = "Error in template * parameter too long", | tooLong = "Error in template * parameter too long", | ||
tooShort = "Error in template * parameter too short", | tooShort = "Error in template * parameter too short", | ||
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 | unknownRule = "#invoke:TemplatePar unknown rule" | ||
} | } | ||
l10nDef[ "de" ] = { | l10nDef[ "de" ] = { | ||
badPattern = "#invoke:TemplatePar | badPattern = "#invoke:TemplatePar Syntaxfehler des pattern", | ||
dupOpt = "#invoke:TemplatePar | dupOpt = "#invoke:TemplatePar Optionsparameter wiederholt", | ||
dupRule = "#invoke:TemplatePar | dupRule = "#invoke:TemplatePar Konflikt key/pattern", | ||
empty = "Fehler bei Vorlage * Pflichtparameter ohne Wert", | empty = "Fehler bei Vorlage * Pflichtparameter ohne Wert", | ||
invalid = "Fehler bei Vorlage * Parameter ungültig", | invalid = "Fehler bei Vorlage * Parameter ungültig", | ||
invalidPar = "#invoke:TemplatePar | invalidPar = "#invoke:TemplatePar Ungültiger Parameter", | ||
minmax = "#invoke:TemplatePar | minmax = "#invoke:TemplatePar min > max", | ||
multiSpell = "Fehler bei Vorlage * Mehrere Parameter-Schreibweisen", | multiSpell = "Fehler bei Vorlage * Mehrere Parameter-Schreibweisen", | ||
noErrorCat = "#invoke:TemplatePar | noErrorCat = "#invoke:TemplatePar noError und keine Kategorie", | ||
noname = "#invoke:TemplatePar | noname = "#invoke:TemplatePar Parameter nicht angegeben", | ||
tooLong = "Fehler bei Vorlage * Parameter zu lang", | tooLong = "Fehler bei Vorlage * Parameter zu lang", | ||
tooShort = "Fehler bei Vorlage * Parameter zu kurz", | tooShort = "Fehler bei Vorlage * Parameter zu kurz", | ||
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 | unknownRule = "#invoke:TemplatePar Unbekannte Regel" | ||
} | } | ||
local Patterns = { | local Patterns = { | ||
Zeile 209: | Zeile 210: | ||
return r | return r | ||
end -- fault() | end -- 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() | |||
Zeile 273: | Zeile 399: | ||
return r | return r | ||
end -- fetch() | end -- 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 r | |||
end -- figure() | |||
Zeile 390: | Zeile 545: | ||
end -- for k, v | end -- for k, v | ||
if r then | if r then | ||
r = failure( "unknown", r, options ) | r = failure( "unknown", "'" .. r .. "'", options ) | ||
else -- all names valid | else -- all names valid | ||
local i, s | local i, s | ||
Zeile 516: | Zeile 671: | ||
-- Uses: | -- Uses: | ||
-- fold() | -- fold() | ||
-- fetch() | -- fetch() | ||
-- fix() | -- fix() | ||
Zeile 553: | Zeile 707: | ||
-- analyze -- string to be analyzed | -- analyze -- string to be analyzed | ||
-- options -- table or nil; optional details | -- options -- table or nil; optional details | ||
-- options.say | -- options.say | ||
-- options.min | -- options.min | ||
Zeile 562: | Zeile 714: | ||
-- false if valid or no answer permitted | -- false if valid or no answer permitted | ||
-- Uses: | -- Uses: | ||
-- | -- feasible() | ||
-- failure() | -- failure() | ||
local r = feasible( analyze, options, false ) | |||
local show | |||
local r = | |||
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 | ||
Zeile 668: | Zeile 768: | ||
-- false if valid or no answer permitted | -- false if valid or no answer permitted | ||
-- Uses: | -- Uses: | ||
-- format() | -- format() | ||
-- failure() | -- failure() | ||
Zeile 688: | Zeile 785: | ||
return r | return r | ||
end -- formatted() | end -- formatted() | ||
Zeile 702: | Zeile 798: | ||
-- form() | -- form() | ||
-- mw.text.trim() | -- mw.text.trim() | ||
-- failure() | |||
-- TemplatePar.assert() | -- TemplatePar.assert() | ||
-- TemplatePar.valid() | -- TemplatePar.valid() | ||
Zeile 712: | Zeile 809: | ||
"noError", | "noError", | ||
"template" }, | "template" }, | ||
template = "#invoke:TemplatePar|".. action .. "|" | template = "#invoke:TemplatePar|".. action .. "|" | ||
} | } | ||
local r = form( false, options ) | local r = form( false, options ) | ||
if not r then | if not r then | ||
local s | local s | ||
options = { cat = frame.args.cat, | options = { cat = frame.args.cat, | ||
low = frame.args.low, | low = frame.args.low, | ||
Zeile 722: | Zeile 819: | ||
template = frame.args.template | template = frame.args.template | ||
} | } | ||
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*$" ) | ||
Zeile 861: | Zeile 948: | ||
-- mw.getCurrentFrame() | -- mw.getCurrentFrame() | ||
-- frame:getParent() | -- frame:getParent() | ||
-- | -- flat() | ||
local t = mw.getCurrentFrame():getParent() | local t = mw.getCurrentFrame():getParent() | ||
return flat( t.args, options ) | return flat( t.args, options ) | ||
Zeile 959: | Zeile 1.044: | ||
"noError", | "noError", | ||
"template" }, | "template" }, | ||
template = "#invoke:TemplatePar|check|" | template = "#invoke:TemplatePar|check|" | ||
} | } | ||
local r = form( false, options ) | local r = form( false, options ) | ||
Zeile 996: | Zeile 1.081: | ||
return tostring( TemplatePar.countNotEmpty() ) | return tostring( TemplatePar.countNotEmpty() ) | ||
end -- .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() | |||