Модуль:parameters
Экспорттолгон функциялар
[түзөтүү]Бул модул аргументтерди иштеп чыгууну жана текшерүүнү стандартташтыруу үчүн колдонулат. Кадимки иш процесси төмөнкүдөй (негизделген Module:translations боюнча):
...
local parent_args = frame:getParent().args
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = true,
[3] = {list = true},
["alt"] = true,
["id"] = true,
["sc"] = {type = "script"},
["tr"] = true,
["ts"] = true,
["lit"] = true,
}
local args = require("Module:parameters").process(parent_args, params)
-- Do further processing of the parsed arguments in <code>args</code>.
...
Params таблицасында ачкыч катары параметр аттары жана параметр тегдеринин (мүмкүн бош) таблицасы болушу керек. баалуулук. Бош таблица маани катары жөн гана параметр бар экенин айтат, бирок эч кандай өзгөчөлүктү кабыл албашы керек дарылоо; кааласа, бош таблицаларды аткарууну оптималдаштыруу катары "true" мааниси менен алмаштырса болот.
Мүмкүн болгон параметр тэгдери төмөндө келтирилген:
required = true- параметр талап кылынат; эгерде ал жок болсо, ката көрсөтүлөт. Калыптын баракчасынын өзү өзгөчө; жок ката ошол жерде көрсөтүлөт.
default =- Эгерде ал жок же бош болсо, параметр үчүн демейки киргизүү маанисин белгилейт. Бул болгон сыяктуу иштетилет анын ордуна киргизүү, ошондуктан (мисалы)
default = "und"түрү менен"language"тил объектисин кайтарат Белгисиз тил эгер тил коду берилбесе. Тизмеде колдонулганда параметрлер, бул тизмедеги биринчи нерсе үчүн гана демейки маанини көрсөтөт. мүмкүн эмес экенин белгилей кетүү керек башка параметрлердин маанисине жараша демейки түзүү.required = trueменен бирге колдонулса, демейки шаблон баракчаларына гана тиешелүү (кийинки жазууну караңыз), "талап кылынган" параметрлердин терс таасири катары шаблон беттеринде талап кылынбайт. Бул шаблондун үлгүсүн көрсөтүү үчүн колдонулушу мүмкүн шаблон барагына кирди; бирок түшүнүктүү болуу үчүн бул максат үчүнtemplate_defaultколдонуу артык. template_default =- Калып беттеринде гана жок же бош параметрлер үчүн демейки киргизүү маанисин белгилейт. Үлгү барактар - бул каалаган барак шаблон мейкиндиги ('Шаблон:' менен башталат) документация барактарынан тышкары ('.../documentation' менен аяктаган). Бул шаблон барагына киргенде талап кылынбаган параметр үчүн мисал маанисин берүү үчүн колдонулушу мүмкүн. шаблондун башка колдонулушуна тоскоолдук кылуу.
template_defaultжанаdefaultэкөө тең бирдей көрсөтүлүшү мүмкүн параметр. Бул жасалса,template_defaultшаблон беттеринде, ал эмидемейкибашка барактарда колдонулат. Мисал катары,{{cs-IPA}}демейки менен камсыз кылуу үчүн[1] = {демейки = "+", template_default = "příklad"}эквивалентин колдонот Негизги мейкиндик жана документация беттери үчүн"+"(модулга|pagename=маанисин колдонууну айтат параметр, анык беттин атына кайтып келет), бирок"příklad"(бул "мисалы" дегенди билдирет), Калып:cs-IPAда. alias_of =- Параметрди башканын лакап аты катары караңыз. Бул параметр үчүн аргументтер көрсөтүлгөндө, алар автоматтык түрдө болот аталышы өзгөртүлүп, лакап ат менен сакталат. Бул дагы эле бир нече альтернативалуу аттары бар параметрлерге мүмкүндүк берет аларга бир гана аты бар сыяктуу мамиле кылуу. Каймана ат коюлган параметрдин конверсияга байланышкан касиеттери (мис. `түр',
set,convert,sublist) лакап аттан алынган жана тиешелүү касиеттер лакап аттын өзүнө коюлган этибарга алынбайт; бирок лакап аттын башка касиеттери лакап аттын спецификациясынан эмес, лакап аттын спецификациясынан алынат. Бул мисалы, эгер сиз тизме параметринин лакап атын түзсөңүз, лакап ат "тизме" касиетин да көрсөтүшү керек дегенди билдирет. же бул тизме эмес. (Мындай учурда, лакап ат үчүн көрсөтүлгөн маани лакап аттын тизмесинин биринчи пунктуна кирет. Сиз тизмеден тышкары параметрдин лакап атын жасай албайсыз; бул катаны ыргытууга алып келет.) Ушундай эле, эгер сиз белгилесеңиз 'separate_no_index' лакап атта эмес, бирок лакап атта эмес, индекстелбеген псевдоним параметринин колдонулушу лакап атта сакталат..defaultачкычы, бирок индекстелбеген лакап аттын колдонулушу лакап аттын тизмесинин биринчи номерленген ачкычында сакталат. Лакап аттарды талап кылуу мүмкүн эмес, анткени бул параметрдин башка аталышын же атын колдонууга жол бербейт. Параметрлер лакап аттар жана ошол эле учурда талап кылынган каталар ыргытылат. allow_empty = true- Эгерде аргумент бош сап мааниси болсо, ал
nilдегенге айландырылбайт, бирок ошол бойдон сакталат.allow_emptyколдонуу болуп саналат бир түрү көрсөтүлгөн болсо, тыюу салынат жана ката ыргытылат. no_trim = true- Позициялык параметрдин башындагы жана аягындагы боштуктар жана жаңы сызыктар сыяктуу интервал символдору алынып салынбайт. (MediaWiki өзү аталган параметрлердин четиндеги боштуктарды жана жаңы сызыктарды автоматтык түрдө кыскартат.)
no_trimколдонулушу бир түрү көрсөтүлгөн болсо, тыюу салынат жана ката ыргытылат. type =- Аргументти кандай маани түрүнө айландырууну белгилейт. Демейки - аны текст сап катары калтыруу. Альтернативалар:
type = "boolean"- Маани логикалык маани катары каралат, чын же жалган. Маани жок, бош сап жана саптар
"0","no","n","false","f"and"off"are treated asfalse, all other values are consideredtrue. type = "number"- The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may be signed (
+or-), and may contain decimal points and leading zeroes. Ifallow_hex = true, then hexadecimal values in the form"0x100"may optionally be used instead, which otherwise have the same syntax restrictions (including signs, decimal digits, and leading zeroes after"0x"). Hexadecimal inputs are not case-sensitive. Lua's special number values (infandnan) are not possible inputs. type = "language"- The value is interpreted as a full or etymology-only language code language code (or name, if
method = "name") and converted into the corresponding object (see Module:languages). If the code or name is invalid, then an error is thrown. The additional settingfamily = truecan be given to allow language family codes to be considered valid and the corresponding object returned. Note that to distinguish an etymology-only language object from a full language object, useobject:hasType("language", "etymology-only"). type = "full language"- The value is interpreted as a full language code (or name, if
method = "name") and converted into the corresponding object (see Module:languages). If the code or name is invalid, then an error is thrown. Etymology-only languages are not allowed. The additional settingfamily = truecan be given to allow language family codes to be considered valid and the corresponding object returned. type = "Wikimedia language"- The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an error is thrown. If
fallback = trueis specified, conventional language codes which are different from their Wikimedia equivalent will also be accepted as a fallback. type = "family"- The value is interpreted as a language family code (or name, if
method = "name") and converted into the corresponding object (see Module:families). If the code or name is invalid, then an error is thrown. type = "script"- The value is interpreted as a script code (or name, if
method = "name") and converted into the corresponding object (see Module:scripts). If the code or name is invalid, then an error is thrown. type = "title"- The value is interpreted as a page title and converted into the corresponding object (see the Title library). If the page title is invalid, then an error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are:
namespace = n- The default namespace, where
nis a namespace number; this is treated as0(the mainspace) if not specified. allow_external = true- External titles are treated as valid.
prefix = "namespace override"(default)- The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance, the input
"Foo"with namespace10returns"Template:Foo","Wiktionary:Foo"returns"Wiktionary:Foo", and"Template:Foo"returns"Template:Foo". Interwiki prefixes cannot act as overrides, however: the input"fr:Foo"returns"Template:fr:Foo". prefix = "force"- The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed. This is the way that
{{[[Template:#invoke:|#invoke:]]}}works when calling modules from the module namespace (828): the input"Foo"returns"Module:Foo","Wiktionary:Foo"returns"Module:Wiktionary:Foo", and"Module:Foo"returns"Module:Module:Foo". prefix = "full override"- The same as
prefix = "namespace override", except that interwiki prefixes can also act as overrides. For instance,"el:All topics"with namespace14returns"el:Category:All topics". Due to the limitations of MediaWiki, only the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is an interwiki prefix: e.g."el:Template:All topics"with namespace14returns"el:Category:Template:All topics".
type = "parameter"- The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when constructing a
frame.argstable of arguments. This means that integers will be converted to numbers, but all other arguments will remain as strings (e.g."1"will be normalized to1, but"foo"and"1.5"will remain unchanged). Note that Scribunto also trims parmeter names, following the same trimming method that this module applies by default to all parameter types. - This type is useful when one set of input arguments is used to construct a
paramstable for use in a subsequentexport.process()call with another set of input arguments; for instance, the set of valid parameters for a template might be defined as{{[[Template:#invoke:[some module]|#invoke:[some module]]]}}in the template, whereargsis a sublist of valid parameters for the template. type = "qualifier"- The value is interpreted as a qualifier and converted into the correct format for passing into
format_qualifiers()in Module:qualifier (which currently just means converting it to a one-item list). type = "labels"- The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into
show_labels()in Module:labels (which is currently a list of strings). Splitting is done on commas not followed by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace. This type should be used by for normal labels (typically specified using|l=or|ll=) and accent qualifiers (typically specified using|a=and|aa=). type = "references"- The value is interpreted as one or more references, in the format prescribed by
parse_references()in Module:references, and converted into a list of objects of the form accepted byformat_references()in the same module. If a syntax error is found in the reference format, an error is thrown. type = function(val) ... endtypemay be set to a function (or callable table), which must take the argument value as its sole argument, and must output one of the other recognized types. This is particularly useful for lists (see below), where certain values need to be interpreted differently to others.
list =- Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The parameters will have a number at the end, except optionally for the first (but see also
require_index = true). For example,list = trueon a parameter named "head" will include the parameters|head=(or|head1=),|head2=,|head3=and so on. If the parameter name is a number, another number doesn't get appended, but the counting simply continues, e.g. for parameter3the sequence is|3=,|4=,|5=etc. List parameters are returned as numbered lists, so for a template that is given the parameters|head=a|head2=b|head3=c, the processed value of the parameter"head"will be{ "a", "b", "c" }}. - The value for
list =can also be a string. This tells the module that parameters other than the first should have a different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example would be for genders:list = "g"on a parameter named1would have parameters|1=,|g2=,|g3=etc. - If the number is not located at the end, it can be specified by putting
"\1"at the number position. For example, parameters|f1accel=,|f2accel=, ... can be captured by using the parameter name"f\1accel", as is done in Module:headword/templates. set =- Параметрдин мааниси көрсөтүлгөн маанилердин тизмесинин бири болушун талап кылуу (же
талап = чындыкболбосо, алынып салынды. берилген). Көрсөтүлгөн тизмедеги маанилер чийки параметрдин маанилерине дал келген саптар болушу керек, учурдан тышкарыtype = "number", бул учурда алар сандар болушу керек. Тизмедеги жеке маани лакап ат тизмеси да болушу мүмкүн, бул тизме, анда биринчи маани "канондук" маани, ал эми калгандары лакап аттар. Качан лакап аттардын бири колдонулса, кайтарылган аргументтердин структурасында алынган параметр талаасы канондук мааниге ээ болот. колдонуу Эгердеtype = "boolean"жана ката ыргытылышына алып келсе,setуруксат берилбейт. sublist =- Параметрдин мааниси өзүнчө чийки маанилердин бөлгүч менен бөлүнгөн тизмеси.
argsичинде пайда болгон талаа болот айландырылган маанилердин Луа тизмеси (б.а. сандык индекстери бар таблица) болуңуз. Эгерsublist = trueберилсе, маанилер үтүргө бөлүнөт (мүмкүн үтүрдүн бир же эки тарабында бош боштук болушу мүмкүн, ал этибарга алынбайт). Эгердекошумча тизме = "боштуксуз үтүр"берилген, маанилер үтүрлөргө бөлүнөт, алардан кийин боштук жок, жана алардын алдында качуучу арткы сызык жок. Болбосо, "кошумча тизменин" мааниси же Луа үлгүсү болушу керек бөлүү үчүн бөлүүчүнү (ларды) же бөлүү үчүн функцияны (же чакыра турган таблицаны) көрсөтүү, ага эки маани берилген (бөлүнүүчү маани жана ката жөнүндө сигнал берүүчү функция) жана бөлүнгөн маанилердин тизмесин кайтарышы керек. convert =- Эгер берилсе, бул чийки параметр маанисин колдонулган Lua объектисине айландыруу үчүн функцияны (же чакыра турган таблицаны) көрсөтөт. андан ары кайра иштетүү учурунда. Функцияга эки аргумент берилет, чийки параметр маанисинин өзү жана колдонулган функция талдоо же конвертациялоо учурунда катаны билдирет жана бир маанини, айландырылган параметрди кайтарышы керек. Ката сигнализациясы Функция генерациялаган билдирүүгө камтылган параметрдин атын жана чийки маанисин камтыйт, андыктан булардын кереги жок ага жиберилген билдирүүдө көрсөтүлгөн. Эгерде "type" "конверттөө" менен бирге көрсөтүлсө, иштетүү
типбиринчи болот. Эгерде "sublist" "convert" менен бирге берилсе, чийки параметрдин мааниси бөлүнөт ылайыктуу жана ар бир натыйжада пунктка чакырылган "конверттөө". allow_hex = truetype = "number"менен бирге колдонулганда,"0x100"форматында он алтылык сандарды киргизүүгө уруксат берет (бул тамга сезгич эмес).family = truetype = "language"менен бирге колдонулганда, тил үй-бүлө коддору кайтып келди. Берилген объект тил үй-бүлөсүнө тиешелүү экендигин текшерүү үчүнobject:hasType("family")колдонуңуз.method = "name"type = "language",type = "family"жеtype = "script"менен бирге колдонулганда, текшерет жана талдайт коддун ордуна тил, үй-бүлө же скрипт аты.allow_holes = true- Бул тизме тибиндеги параметрлер менен бирге колдонулат. Демейки боюнча, баалуулуктар натыйжада тыгыз топтолот тизме. Бул, мисалы,
|head2=эмес,head=a|head3=cдеген жазуу көрсөтүлсө, кайтарылган тизме Калып:"a", "c"} болот, маанилер1жана3эмес,1жана2индекстеринде сакталат. Эгерде аны сактоо кааласа номерлөө бузулбаган, мисалы, бир нече тизме параметрлеринин сандары бири-бири менен корреляцияланган болсо (мисалы,{{affix}}), анда бул тег көрсөтүлүшү керек. - Эгер
allow_holes = trueберилсе, эки чыныгы маанинин ортосундаnilмаанилер болушу мүмкүн, бул Луанын көбүн түзөт#жеipairs()сыяктуу таблицаны иштетүү функциялары иштебей калды. Муну оңдоо үчүн, пайда болгон таблицада ан камтылат кошумча аталган маани,maxindex, ал сизге таблицадагы эң жогорку сандык индексти билдирет. Ичинде Жогорудагы мисал, натыйжада таблица эми Калып:"a", nil, "c", maxindex = 3} болот. Ушундай жол менен, сиз кайталай аласыз1денmaxindexге чейинки маанилер, алардын ортосундаnilмаанилерди өткөрүп жиберүү. disallow_holes = true- Бул тизме тибиндеги параметрлер менен бирге колдонулат. Жогоруда айтылгандай, адатта, булакта тешик бар болсо аргументтер, мис.
head=a|head3=cбирок|head2=эмес, ал кайтарылган тизмеден алынып салынат. Эгердеdisallow_holes = trueкөрсөтүлгөн, бирок мындай учурда ката кетирилет. Бул ошол жерде колдонулушу керек тизилиши керек болгон бир нече тизме тибиндеги параметрлер (мисалы,|head=жана|tr=экөө тең жеткиликтүү жана|head3=|tr3=менен сапталат), эгердеallow_holes = trueберилбесе жана сиз чечүүгө даярсыз кайтарылган тизмелерде тешиктер. disallow_missing = true- Бул
disallow_holes = trueдегенге окшош, бирок аргумент бош болсо, ката кетирилбейт. толугу менен жок. Бул кээде тизмеде пайда болгон аралык бош сандык параметрлерди көтөрүү үчүн колдонулушу мүмкүн шаблондор. Мисалы,head=a|head2=|head3=cката кетирбейт, бирокhead=a|head3=cката кетирет. require_index = true- Бул тизме тибиндеги параметрлер менен бирге колдонулат. Демейки боюнча, биринчи параметр анын индексин өткөрүп жибериши мүмкүн. Мисалы,
headдеп аталган тизме параметринин биринчи параметри|head=же|head1=. Эгерrequire_index = trueкөрсөтүлсө, бирок|head1=гана таанылат жана|head=белгисиз параметр катары каралат.{{affixusex}}(жана варианттары{{suffixusex}},{{prefixusex}}) колдонуңуз бул, мисалы, бардык тизме параметрлери боюнча. separate_no_index = true- Бул
|head=менен|head1=башка параметрлерди айырмалоо үчүн колдонулат. Мисалы, в{{affixusex}},|sc=(usex тилиндеги бардык элементтер үчүн скрипт коду) жана|sc1=(биринчи элементтин скрипт коду, биринчи элементке тил коду менен префикс коюлганда колдонулат башка тилде экенин көрсөтүп турат). Бул колдонулганда, пайда болгон таблица кошумча аталышты камтыйт индекссиз аргументтин маанисин камтыган "демейки" маани. demo = true- Бул параметр шаблондун өз бетинде гана иштетилгенин камсыз кылуунун бир жолу катары колдонулат (жана анын документтери бет) жана Колдонуучуда: аттар мейкиндиги; антпесе, ал белгисиз параметр катары каралат. Бул учурда гана колдонулушу керек шаблонду анын документтеринде көрсөтүү үчүн атайын орнотуулар талап кылынат (мисалы, беттин атын тууралоо же өчүрүү категориялар). Көпчүлүк учурларда, муну демо параметрлерди колдонбостон жасоо мүмкүн болушу керек, бирок алар болушу мүмкүн шаблон/документация бетинде ошол эле шаблондун реалдуу колдонулушу да камтылган болсо (мис.,
{{shortcut}}), талап кылынат. аларды айырмалоонун бир жолу катары.
export.convert_val
[түзөтүү]function export.convert_val(val, name, param)
Өткөрүлгөн params таблицасында тизмеленген тиешелүү спецификацияларга ылайык параметрдин маанисин айландырыңыз Модуль:parameters. val аты name (ката билдирүүлөрүндө гана колдонулат) болгон параметр үчүн конвертациялануучу маани. param - спецификация (параметр үчүн params таблицасынын маани бөлүгү). Параметрдин аталышындагы өтүүнүн ордуна, name ката кетирүүчү функция болушу мүмкүн, параметр аты жана мааниси менен бирге көрсөтүлгөн билдирүүнү көрсөтөт. Бул функция 'param' ичиндеги бардык конверсияга байланыштуу талааларды иштетет, анын ичинде 'түр', 'коюу', 'кошумча тизме', 'конверттөө', ж.б. Бул конверттелген маанини кайтарат.
export.process
[түзөтүү]function export.process(args, params, return_unknown)
Process arguments with a given list of parameters. Return a table containing the processed arguments. The args parameter specifies the arguments to be processed; they are the arguments you might retrieve from frame:getParent().args (the template arguments) or in some cases frame.args (the invocation arguments). The params parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in the parameter table, an error is thrown.
The structure of the params table is as described above in the intro comment.
WARNING: The params table is destructively modified to save memory. Nonetheless, different keys can share the same value objects in memory without causing problems.
The return_unknown parameter, if set to true, prevents the function from triggering an error when it comes across an argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of the remainder to the function that handles a specific inflectional type.
--[==[TODO:
* Change certain flag names, as some are misnomers:
* Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`.
* Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted.
* Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that.
]==]
local export = {}
local debug_track_module = "Module:debug/track"
local families_module = "Module:families"
local function_module = "Module:fun"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local math_module = "Module:math"
local pages_module = "Module:pages"
local parse_utilities_module = "Module:parse utilities"
local references_module = "Module:references"
local scripts_module = "Module:scripts"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local wikimedia_languages_module = "Module:wikimedia languages"
local yesno_module = "Module:yesno"
local mw = mw
local mw_title = mw.title
local string = string
local table = table
local dump = mw.dumpObject
local find = string.find
local format = string.format
local gmatch = string.gmatch
local gsub = string.gsub
local insert = table.insert
local ipairs = ipairs
local list_to_text = mw.text.listToText
local make_title = mw_title.makeTitle
local match = string.match
local max = math.max
local maxn = table.maxn
local new_title = mw_title.new
local next = next
local pairs = pairs
local pcall = pcall
local rawset = rawset
local require = require
local sort = table.sort
local sub = string.sub
local tonumber = tonumber
local traceback = debug.traceback
local type = type
local current_title_text, current_namespace -- Керек болгондо аныкталат.
local namespaces = mw.site.namespaces
--[==[
Башка модулдардагы функциялар үчүн жүктөгүчтөр, алар чакырганда максаттуу функциянын үстүнөн жазылат. Бул модулдардын керек болгондо гана жүктөлүшүн камсыздайт, локалдык түрдө жарыяланган алдын ала жүктөлгөн функциялардын ылдамдыгын/ынгайлуулугун сактап калат жана биринчи чалуудан кийин эч кандай кошумча чыгым болбойт, анткени максаттуу функциялар ар кандай кийинки чалууларда түздөн-түз чакырылат.]==]
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function extend(...)
extend = require(table_module).extend
return extend(...)
end
local function get_family_by_code(...)
get_family_by_code = require(families_module).getByCode
return get_family_by_code(...)
end
local function get_family_by_name(...)
get_family_by_name = require(families_module).getByCanonicalName
return get_family_by_name(...)
end
local function get_language_by_code(...)
get_language_by_code = require(languages_module).getByCode
return get_language_by_code(...)
end
local function get_language_by_name(...)
get_language_by_name = require(languages_module).getByCanonicalName
return get_language_by_name(...)
end
local function get_script_by_code(...)
get_script_by_code = require(scripts_module).getByCode
return get_script_by_code(...)
end
local function get_script_by_name(...)
get_script_by_name = require(scripts_module).getByCanonicalName
return get_script_by_name(...)
end
local function get_wm_lang_by_code(...)
get_wm_lang_by_code = require(wikimedia_languages_module).getByCode
return get_wm_lang_by_code(...)
end
local function get_wm_lang_by_code_with_fallback(...)
get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback
return get_wm_lang_by_code_with_fallback(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function is_callable(...)
is_callable = require(function_module).is_callable
return is_callable(...)
end
local function is_finite_real_number(...)
is_finite_real_number = require(math_module).is_finite_real_number
return is_finite_real_number(...)
end
local function is_integer(...)
is_integer = require(math_module).is_integer
return is_integer(...)
end
local function is_internal_title(...)
is_internal_title = require(pages_module).is_internal_title
return is_internal_title(...)
end
local function is_positive_integer(...)
is_positive_integer = require(math_module).is_positive_integer
return is_positive_integer(...)
end
local function iterate_list(...)
iterate_list = require(table_module).iterateList
return iterate_list(...)
end
local function num_keys(...)
num_keys = require(table_module).numKeys
return num_keys(...)
end
local function parse_references(...)
parse_references = require(references_module).parse_references
return parse_references(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities_module).pattern_escape
return pattern_escape(...)
end
local function php_trim(...)
php_trim = require(string_utilities_module).php_trim
return php_trim(...)
end
local function scribunto_param_key(...)
scribunto_param_key = require(string_utilities_module).scribunto_param_key
return scribunto_param_key(...)
end
local function sorted_pairs(...)
sorted_pairs = require(table_module).sortedPairs
return sorted_pairs(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function split_labels_on_comma(...)
split_labels_on_comma = require(labels_module).split_labels_on_comma
return split_labels_on_comma(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
--[==[ intro:
Бул модул аргументтерди иштеп чыгууну жана текшерүүнү стандартташтыруу үчүн колдонулат. Кадимки иш процесси төмөнкүдөй (негизделген [[Module:translations]] боюнча):
{
...
local parent_args = frame:getParent().args
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = true,
[3] = {list = true},
["alt"] = true,
["id"] = true,
["sc"] = {type = "script"},
["tr"] = true,
["ts"] = true,
["lit"] = true,
}
local args = require("Module:parameters").process(parent_args, params)
-- Do further processing of the parsed arguments in `args`.
...
}
`Params` таблицасында ачкыч катары параметр аттары жана параметр тегдеринин (мүмкүн бош) таблицасы болушу керек.
баалуулук. Бош таблица маани катары жөн гана параметр бар экенин айтат, бирок эч кандай өзгөчөлүктү кабыл албашы керек
дарылоо; кааласа, бош таблицаларды аткарууну оптималдаштыруу катары "true" мааниси менен алмаштырса болот.
Мүмкүн болгон параметр тэгдери төмөндө келтирилген:
; {required = true}
: параметр талап кылынат; эгерде ал жок болсо, ката көрсөтүлөт. Калыптын баракчасынын өзү өзгөчө; жок
ката ошол жерде көрсөтүлөт.
; {default =}
: Эгерде ал жок же бош болсо, параметр үчүн демейки киргизүү маанисин белгилейт. Бул болгон сыяктуу иштетилет
анын ордуна киргизүү, ошондуктан (мисалы) {default = "und"} түрү менен {"language"} тил объектисин кайтарат
[[:Категория:Белгисиз тил|Белгисиз тил]] эгер тил коду берилбесе. Тизмеде колдонулганда
параметрлер, бул тизмедеги биринчи нерсе үчүн гана демейки маанини көрсөтөт. мүмкүн эмес экенин белгилей кетүү керек
башка параметрлердин маанисине жараша демейки түзүү. {required = true} менен бирге колдонулса, демейки
шаблон баракчаларына гана тиешелүү (кийинки жазууну караңыз), "талап кылынган" параметрлердин терс таасири катары
шаблон беттеринде талап кылынбайт. Бул шаблондун үлгүсүн көрсөтүү үчүн колдонулушу мүмкүн
шаблон барагына кирди; бирок түшүнүктүү болуу үчүн бул максат үчүн `template_default` колдонуу артык.
; {template_default =}
: Калып беттеринде гана жок же бош параметрлер үчүн демейки киргизүү маанисин белгилейт. Үлгү барактар - бул каалаган барак
шаблон мейкиндиги ('Шаблон:' менен башталат) документация барактарынан тышкары ('.../documentation' менен аяктаган).
Бул шаблон барагына киргенде талап кылынбаган параметр үчүн мисал маанисин берүү үчүн колдонулушу мүмкүн.
шаблондун башка колдонулушуна тоскоолдук кылуу. `template_default` жана `default` экөө тең бирдей көрсөтүлүшү мүмкүн
параметр. Бул жасалса, `template_default` шаблон беттеринде, ал эми `демейки` башка барактарда колдонулат. Мисал катары,
{{tl|cs-IPA}} демейки менен камсыз кылуу үчүн {[1] = {демейки = "+", template_default = "příklad"}} эквивалентин колдонот
Негизги мейкиндик жана документация беттери үчүн {"+"} (модулга {{para|pagename}} маанисин колдонууну айтат
параметр, анык беттин атына кайтып келет), бирок {"příklad"} (бул "мисалы" дегенди билдирет), [[Калып:cs-IPA]]да.
; {alias_of =}
: Параметрди башканын лакап аты катары караңыз. Бул параметр үчүн аргументтер көрсөтүлгөндө, алар автоматтык түрдө болот
аталышы өзгөртүлүп, лакап ат менен сакталат. Бул дагы эле бир нече альтернативалуу аттары бар параметрлерге мүмкүндүк берет
аларга бир гана аты бар сыяктуу мамиле кылуу. Каймана ат коюлган параметрдин конверсияга байланышкан касиеттери (мис. `түр',
`set`, `convert`, `sublist`) лакап аттан алынган жана тиешелүү касиеттер лакап аттын өзүнө коюлган
этибарга алынбайт; бирок лакап аттын башка касиеттери лакап аттын спецификациясынан эмес, лакап аттын спецификациясынан алынат. Бул
мисалы, эгер сиз тизме параметринин лакап атын түзсөңүз, лакап ат "тизме" касиетин да көрсөтүшү керек дегенди билдирет.
же бул тизме эмес. (Мындай учурда, лакап ат үчүн көрсөтүлгөн маани лакап аттын тизмесинин биринчи пунктуна кирет.
Сиз тизмеден тышкары параметрдин лакап атын жасай албайсыз; бул катаны ыргытууга алып келет.) Ушундай эле, эгер сиз белгилесеңиз
'separate_no_index' лакап атта эмес, бирок лакап атта эмес, индекстелбеген псевдоним параметринин колдонулушу лакап атта сакталат.
`.default` ачкычы, бирок индекстелбеген лакап аттын колдонулушу лакап аттын тизмесинин биринчи номерленген ачкычында сакталат.
Лакап аттарды талап кылуу мүмкүн эмес, анткени бул параметрдин башка аталышын же атын колдонууга жол бербейт. Параметрлер
лакап аттар жана ошол эле учурда талап кылынган каталар ыргытылат.
; {allow_empty = true}
: Эгерде аргумент бош сап мааниси болсо, ал {nil} дегенге айландырылбайт, бирок ошол бойдон сакталат. `allow_empty` колдонуу болуп саналат
бир түрү көрсөтүлгөн болсо, тыюу салынат жана ката ыргытылат.
; {no_trim = true}
: Позициялык параметрдин башындагы жана аягындагы боштуктар жана жаңы сызыктар сыяктуу интервал символдору алынып салынбайт.
(MediaWiki өзү аталган параметрлердин четиндеги боштуктарды жана жаңы сызыктарды автоматтык түрдө кыскартат.) `no_trim` колдонулушу
бир түрү көрсөтүлгөн болсо, тыюу салынат жана ката ыргытылат.
; {type =}
: Аргументти кандай маани түрүнө айландырууну белгилейт. Демейки - аны текст сап катары калтыруу. Альтернативалар:
:; {type = "boolean"}
:: Маани логикалык маани катары каралат, чын же жалган. Маани жок, бош сап жана саптар {"0"},
{"no"}, {"n"}, {"false"}, {"f"} and {"off"} are treated as {false}, all other values are considered {true}.
:; {type = "number"}
:: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may
be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal
values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions
(including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's
special number values (`inf` and `nan`) are not possible inputs.
:; {type = "language"}
:: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code
language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]).
If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow
[[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned.
Note that to distinguish an etymology-only language object from a full language object, use
{object:hasType("language", "etymology-only")}.
:; {type = "full language"}
:: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding
object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages
are not allowed. The additional setting {family = true} can be given to allow
[[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned.
:; {type = "Wikimedia language"}
:: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an
error is thrown. If {fallback = true} is specified, conventional language codes which are different from their
Wikimedia equivalent will also be accepted as a fallback.
:; {type = "family"}
:: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the
corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown.
:; {type = "script"}
:: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object
(see [[Module:scripts]]). If the code or name is invalid, then an error is thrown.
:; {type = "title"}
:: The value is interpreted as a page title and converted into the corresponding object (see the
[[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an
error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are:
::; {namespace = n}
::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified.
::; {allow_external = true}
::: External titles are treated as valid.
::; {prefix = "namespace override"} (default)
::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance,
the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and
{"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"}
returns {"Template:fr:Foo"}.
::; {prefix = "force"}
::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed.
This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"}
returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns
{"Module:Module:Foo"}.
::; {prefix = "full override"}
::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance,
{"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only
the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is
an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}.
:; {type = "parameter"}
:: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when
constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other
arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain
unchanged). Note that Scribunto also trims parmeter names, following the same trimming method that this module
applies by default to all parameter types.
:: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent
{export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template
might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters
for the template.
:; {type = "qualifier"}
:: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()`
in [[Module:qualifier]] (which currently just means converting it to a one-item list).
:; {type = "labels"}
:: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into
`show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed
by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace.
This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent
qualifiers (typically specified using {{para|a}} and {{para|aa}}).
:; {type = "references"}
:: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in
[[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same
module. If a syntax error is found in the reference format, an error is thrown.
:; {type = function(val) ... end}
:: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must
output one of the other recognized types. This is particularly useful for lists (see below), where certain values need
to be interpreted differently to others.
; {list =}
: Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The
parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For
example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}),
{{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but
the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List
parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`,
the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}.
: The value for {list =} can also be a string. This tells the module that parameters other than the first should have a
different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example
would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}}
etc.
: If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example,
parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is
done in [[Module:headword/templates]].
; {set =}
: Параметрдин мааниси көрсөтүлгөн маанилердин тизмесинин бири болушун талап кылуу (же {талап = чындык} болбосо, алынып салынды.
берилген). Көрсөтүлгөн тизмедеги маанилер чийки параметрдин маанилерине дал келген саптар болушу керек, учурдан тышкары
{type = "number"}, бул учурда алар сандар болушу керек. Тизмедеги жеке маани ''лакап ат тизмеси'' да болушу мүмкүн,
бул тизме, анда биринчи маани "канондук" маани, ал эми калгандары лакап аттар. Качан лакап аттардын бири
колдонулса, кайтарылган аргументтердин структурасында алынган параметр талаасы канондук мааниге ээ болот. колдонуу
Эгерде {type = "boolean"} жана ката ыргытылышына алып келсе, `set` уруксат берилбейт.
; {sublist =}
: Параметрдин мааниси өзүнчө чийки маанилердин бөлгүч менен бөлүнгөн тизмеси. `args` ичинде пайда болгон талаа болот
айландырылган маанилердин Луа тизмеси (б.а. сандык индекстери бар таблица) болуңуз. Эгер {sublist = true} берилсе, маанилер
үтүргө бөлүнөт (мүмкүн үтүрдүн бир же эки тарабында бош боштук болушу мүмкүн, ал этибарга алынбайт). Эгерде
{кошумча тизме = "боштуксуз үтүр"} берилген, маанилер үтүрлөргө бөлүнөт, алардан кийин боштук жок,
жана алардын алдында качуучу арткы сызык жок. Болбосо, "кошумча тизменин" мааниси же Луа үлгүсү болушу керек
бөлүү үчүн бөлүүчүнү (ларды) же бөлүү үчүн функцияны (же чакыра турган таблицаны) көрсөтүү, ага эки маани берилген
(бөлүнүүчү маани жана ката жөнүндө сигнал берүүчү функция) жана бөлүнгөн маанилердин тизмесин кайтарышы керек.
; {convert =}
: Эгер берилсе, бул чийки параметр маанисин колдонулган Lua объектисине айландыруу үчүн функцияны (же чакыра турган таблицаны) көрсөтөт.
андан ары кайра иштетүү учурунда. Функцияга эки аргумент берилет, чийки параметр маанисинин өзү жана колдонулган функция
талдоо же конвертациялоо учурунда катаны билдирет жана бир маанини, айландырылган параметрди кайтарышы керек. Ката сигнализациясы
Функция генерациялаган билдирүүгө камтылган параметрдин атын жана чийки маанисин камтыйт, андыктан булардын кереги жок
ага жиберилген билдирүүдө көрсөтүлгөн. Эгерде "type" "конверттөө" менен бирге көрсөтүлсө, иштетүү
`тип` биринчи болот. Эгерде "sublist" "convert" менен бирге берилсе, чийки параметрдин мааниси бөлүнөт
ылайыктуу жана ар бир натыйжада пунктка чакырылган "конверттөө".
; {allow_hex = true}
: {type = "number"} менен бирге колдонулганда, {"0x100"} форматында он алтылык сандарды киргизүүгө уруксат берет (бул
тамга сезгич эмес).
; {family = true}
: {type = "language"} менен бирге колдонулганда, [[Wiktionary:Language family|тил үй-бүлө коддору]]
кайтып келди. Берилген объект тил үй-бүлөсүнө тиешелүү экендигин текшерүү үчүн {object:hasType("family")} колдонуңуз.
; {method = "name"}
: {type = "language"}, {type = "family"} же {type = "script"} менен бирге колдонулганда, текшерет жана талдайт
коддун ордуна тил, үй-бүлө же скрипт аты.
; {allow_holes = true}
: Бул тизме тибиндеги параметрлер менен бирге колдонулат. Демейки боюнча, баалуулуктар натыйжада тыгыз топтолот
тизме. Бул, мисалы, {{para|head2}} эмес, `head=a|head3=c` деген жазуу көрсөтүлсө, кайтарылган тизме
{{"a", "c"}}} болот, маанилер {1} жана {3} эмес, {1} жана {2} индекстеринде сакталат. Эгерде аны сактоо кааласа
номерлөө бузулбаган, мисалы, бир нече тизме параметрлеринин сандары бири-бири менен корреляцияланган болсо (мисалы,
{{tl|affix}}), анда бул тег көрсөтүлүшү керек.
: Эгер {allow_holes = true} берилсе, эки чыныгы маанинин ортосунда {nil} маанилер болушу мүмкүн, бул Луанын көбүн түзөт
{#} же {ipairs()} сыяктуу таблицаны иштетүү функциялары иштебей калды. Муну оңдоо үчүн, пайда болгон таблицада ан камтылат
кошумча аталган маани, `maxindex`, ал сизге таблицадагы эң жогорку сандык индексти билдирет. Ичинде
Жогорудагы мисал, натыйжада таблица эми {{"a", nil, "c", maxindex = 3}}} болот. Ушундай жол менен, сиз кайталай аласыз
{1}ден `maxindex`ге чейинки маанилер, алардын ортосунда {nil} маанилерди өткөрүп жиберүү.
; {disallow_holes = true}
: Бул тизме тибиндеги параметрлер менен бирге колдонулат. Жогоруда айтылгандай, адатта, булакта тешик бар болсо
аргументтер, мис. `head=a|head3=c` бирок {{para|head2}} эмес, ал кайтарылган тизмеден алынып салынат. Эгерде
{disallow_holes = true} көрсөтүлгөн, бирок мындай учурда ката кетирилет. Бул ошол жерде колдонулушу керек
тизилиши керек болгон бир нече тизме тибиндеги параметрлер (мисалы, {{para|head}} жана {{para|tr}} экөө тең жеткиликтүү жана
{{para|head3}} {{para|tr3}} менен сапталат), эгерде {allow_holes = true} берилбесе жана сиз чечүүгө даярсыз
кайтарылган тизмелерде тешиктер.
; {disallow_missing = true}
: Бул {disallow_holes = true} дегенге окшош, бирок аргумент бош болсо, ката кетирилбейт.
толугу менен жок. Бул кээде тизмеде пайда болгон аралык бош сандык параметрлерди көтөрүү үчүн колдонулушу мүмкүн
шаблондор. Мисалы, `head=a|head2=|head3=c` ката кетирбейт, бирок `head=a|head3=c` ката кетирет.
; {require_index = true}
: Бул тизме тибиндеги параметрлер менен бирге колдонулат. Демейки боюнча, биринчи параметр анын индексин өткөрүп жибериши мүмкүн.
Мисалы, `head` деп аталган тизме параметринин биринчи параметри {{para|head}} же
{{para|head1}}. Эгер {require_index = true} көрсөтүлсө, бирок {{para|head1}} гана таанылат жана {{para|head}}
белгисиз параметр катары каралат. {{tl|affixusex}} (жана варианттары {{tl|suffixusex}}, {{tl|prefixusex}}) колдонуңуз
бул, мисалы, бардык тизме параметрлери боюнча.
; {separate_no_index = true}
: Бул {{para|head}} менен {{para|head1}} башка параметрлерди айырмалоо үчүн колдонулат. Мисалы, в
{{tl|affixusex}}, {{para|sc}} (usex тилиндеги бардык элементтер үчүн скрипт коду) жана
{{para|sc1}} (биринчи элементтин скрипт коду, биринчи элементке тил коду менен префикс коюлганда колдонулат
башка тилде экенин көрсөтүп турат). Бул колдонулганда, пайда болгон таблица кошумча аталышты камтыйт
индекссиз аргументтин маанисин камтыган "демейки" маани.
; {demo = true}
: Бул параметр шаблондун өз бетинде гана иштетилгенин камсыз кылуунун бир жолу катары колдонулат (жана анын документтери
бет) жана Колдонуучуда: аттар мейкиндиги; антпесе, ал белгисиз параметр катары каралат. Бул учурда гана колдонулушу керек
шаблонду анын документтеринде көрсөтүү үчүн атайын орнотуулар талап кылынат (мисалы, беттин атын тууралоо же өчүрүү
категориялар). Көпчүлүк учурларда, муну демо параметрлерди колдонбостон жасоо мүмкүн болушу керек, бирок алар болушу мүмкүн
шаблон/документация бетинде ошол эле шаблондун реалдуу колдонулушу да камтылган болсо (мис., {{tl|shortcut}}), талап кылынат.
аларды айырмалоонун бир жолу катары.
]==]
-- Учурдагы барак учурдагы {{#invoke}} камтылган шаблон же модуль болсо, чындыкты кайтарат.
-- Documentation_documentation аргументи берилсе, учурдагы барак же барактын документация барагы болсо, чындыкты кайтарат.
local own_page, own_page_or_documentation
local function is_own_page(include_documentation)
if own_page == nil then
if current_namespace == nil then
local current_title = mw_title.getCurrentTitle()
current_title_text, current_namespace = current_title.prefixedText, current_title.namespace
end
local frame = current_namespace == 828 and mw.getCurrentFrame() or
current_namespace == 10 and mw.getCurrentFrame():getParent()
if frame then
local frame_title_text = frame:getTitle()
own_page = current_title_text == frame_title_text
own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation"
else
own_page, own_page_or_documentation = false, false
end
end
return include_documentation and own_page_or_documentation or own_page
end
local function track(page)
local pages, current = {"parameters/" .. page}
-- Check through the traceback to get the calling module and function.
for mod, func in gmatch(traceback(), "%f[^%z\n]\tModule:(.-):%d+: in function '(.-)'%f[%z\n]") do
if current == nil then
current = mod -- Name of this module.
elseif mod ~= current then
insert(pages, "parameters/" .. page .. "/" .. mod)
-- FIXME: эгерде чакыруучу функция #invoke: тарабынан чакырылган функция болсо, кайра артка кайтаруу аны чыныгы аталышынын ордуна "chunk" деп атайт.
insert(pages, "parameters/" .. page .. "/" .. mod .. "/" .. func)
break
end
end
debug_track(pages)
end
-------------------------------------- Some helper functions -----------------------------
-- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If
-- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies
-- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but
-- internally uses mw.text.listToText().
local function concat_list(list, conjunction, dump_vals)
if dump_vals then
for k, v in pairs(list) do
list[k] = dump(v)
end
end
return list_to_text(list, nil, conjunction)
end
-- Split an argument on comma, but not comma followed by whitespace.
local function split_on_comma_without_whitespace(val)
if find(val, "\\", nil, true) or match(val, ",%s") then
return split_on_comma(val)
end
return split(val, ",")
end
-- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a
-- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise,
-- just return `msg`.
local function msg_with_processed(msg, rawval, processed)
if rawval == processed then
return msg
end
local processed_type = type(processed)
return format("%s (processed value %s)",
msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed)
)
end
-------------------------------------- Error handling -----------------------------
local function process_error(fmt, ...)
local args = {...}
for i, val in ipairs(args) do
args[i] = dump(val)
end
if type(fmt) == "table" then
-- hacky signal that we're called from internal_process_error(), and not to omit stack frames
return error(format(fmt[1], unpack(args)))
end
return error(format(fmt, unpack(args)), 3)
end
local function internal_process_error(fmt, ...)
process_error({"Internal error in `params` table: " .. fmt}, ...)
end
-- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted.
local function validate_name(name, desc, extra_name, is_argument)
local normalized = scribunto_param_key(name)
if name and name == normalized then
if is_argument or type(name) ~= "string" then
return
end
local placeholder = find(name, "\1", nil, true)
if not placeholder then
return
elseif find(name, "\1", placeholder + 1, true) then
error(format(
"Internal error: expected %s to only contain one placeholder, but saw %s",
extra_name and (desc .. dump(extra_name)) or desc, dump(name)
))
end
local first_name = gsub(name, "\1", "1")
normalized = scribunto_param_key(first_name)
if first_name == normalized then
return
end
error(format(
"Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s",
extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized)
))
elseif normalized == nil then
error(format(
"Internal error: expected %s to be of type string or number, but saw %s",
extra_name and (desc .. dump(extra_name)) or desc, type(name)
))
end
error(format(
"Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)",
extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized)
))
end
-- TODO: give ranges instead of long lists, if possible.
local function params_list_error(params, msg)
local list, n = {}, 0
for name in sorted_pairs(params) do
n = n + 1
list[n] = name
end
error(format(
"Parameter%s %s.",
format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)),
msg
), 3)
end
-- convert_val_error() менен колдонуу үчүн жардамчы функция. `concat_list` жана колдонуу менен мүмкүн болгон тандоолордун тизмесин форматтаңыз
-- бирден көп болсо, тандоолордун алдында "же" дегенди көрсөтүүчү "же" байланышы.
local function format_choice_list(valid)
return (#valid > 1 and "either " or "") .. concat_list(valid, " or ")
end
-- Туура эмес "valid" маанисине ката сигнал берүү (ал түрүн көрсөткөн сап, же
-- "коюу" колдонулган учурда мүмкүн болгон маанилердин тизмеси). `name` параметрдин аты жана а болушу мүмкүн
-- катаны белгилөө функциясы (ал параметрдин атын жана маанисин автоматтык түрдө көрсөтөт деп болжолдонот). `seetext` болуп саналат
-- көрсөтүү үчүн кошумча кошумча түшүндүрүүчү шилтеме (мисалы, [[WT:LOL]], мүмкүн болгон тилдердин жана коддордун тизмеси).
local function convert_val_error(val, name, valid, seetext)
if is_callable(name) then
if type(valid) == "table" then
valid = "choice, must be " .. format_choice_list(valid)
end
name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or ""))
else
if type(valid) == "table" then
valid = format_choice_list(valid)
else
valid = "a valid " .. valid
end
error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val),
seetext and " See " .. seetext .. "." or ""))
end
end
-- Параметрдин мааниси `val` жана `name` аталышы берилген тиешелүү ката-сигнал функциясын жаратыңыз. Эгерде `аты` мурунтан эле
-- функция, ал жөн гана кайтарылган; антпесе, берилген билдирүүнү көрсөткөн функция түзүлөт жана кайтарылат
-- параметрдин аты жана мааниси менен бирге.
local function make_parse_err(val, name)
if is_callable(name) then
return name
end
return function(msg)
error(format("%s: parameter %s=%s", msg, name, val))
end
end
-------------------------------------- Наркы конверсия -----------------------------
-- Тизме параметринин "аты" жана "тизме" талаасынын тиешелүү "тизме_аты" мааниси үчүн (бир эле мааниге ээ болушу керек)
-- "ат" катары, эгерде "тизме = чындык" берилген болсо), тизменин параметрлерине дал келүү үчүн үлгү түзүңүз жана үлгү катары сактаңыз
-- "үлгү" баскычын басыңыз, тиешелүү маани "аты" деп коюлган. Мисалы, "тизме_аты" "tr" болсо, үлгү болот
-- "tr" менен "tr1", "tr2", ..., "tr10", "tr11" ж.б. дал келет. Эгерде `тизме_аты` ичинде \1 болсо, сан
-- бөлүгү \1 ордуна келет. Мисалы, "тизме_аты" "f\1accel" болсо, үлгү "faccel" менен дал келет.
-- "f1accel", "f2accel" ж.б. `name` ичиндеги бардык \1 `үлгүлөргө` сактоодон мурун алынып салынат.
local function save_pattern(name, list_name, patterns)
name = type(name) == "string" and gsub(name, "\1", "") or name
if find(list_name, "\1", nil, true) then
patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name
else
patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name
list_name = list_name .. "\1"
end
validate_name(list_name, "the list field of parameter ", name)
return patterns
end
-- `sublist` менен колдонуу үчүн жардамчы функция. Бул бөлүүнү кайтарган for циклинде колдонуу үчүн итератор функциясы
-- `sublist` колдонулган `val` элементтери (Lua бөлүү үлгүсү; бульдик `true` үтүрлөргө бөлүү үчүн ыктыярдуу түрдө тегереги менен курчалган
-- боштук; "Боштуксуз үтүр" үтүрлөргө гана бөлүнөт, андан кийин бошотулбаган боштуктар
-- тескери сызык менен; же бөлүү үчүн функция, ага эки маани берилет, бөлүнүүчү маани жана функция
-- ката сигналы жана бөлүнгөн элементтердин тизмесин кайтарышы керек). `name` - бул параметрдин аты же ката сигналы
-- функция convert_val()га өткөрүлдү.
local function split_sublist(val, name, sublist)
if sublist == true then
return gsplit(val, "%s*,%s*")
elseif sublist == "comma without whitespace" then
sublist = split_on_comma_without_whitespace
elseif type(sublist) == "string" then
return gsplit(val, sublist)
elseif not is_callable(sublist) then
error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist)))
end
return iterate_list(sublist(val, make_parse_err(val, name)))
end
-- `val` мааниси бар `name` деп аталган параметр жана параметр спецификациясы `param` үчүн, эгерде `set` талаасы көрсөтүлгөн болсо,
-- маани `set`те көрсөтүлгөндөрдүн бири болуп саналат жана башка учурда ката кетирет. `name` түз жерден алынат
-- ылайыктуу параметр convert_val()га өтүп, катаны белгилөөчү функция болушу мүмкүн. Кошумча `param_type` болуп саналат
-- `val` конверсия түрүн көрсөткөн сап жана атайын каптоо үчүн колдонулат: Эгерде `param_type` "логикалык" болсо, анда
-- ички ката ыргытылды (анткени `set` логикалык маанилер менен бирге колдонулбайт) жана `param_type` "сан" болсо,
-- эч кандай текшерүү болбойт, анткени бул учурда `set` сандарды камтыйт жана сандарды өзгөртүү функциясынын ичинде текшерилет.
-- өзү, `val` санга айландыргандан кийин. `val`дын канондук маанисин кайтарыңыз (ал `val`дан башкача болушу мүмкүн
-- эгер лакап ат картасы берилсе).
local function check_set(val, name, param, param_type)
if param_type == "boolean" then
error(format('Internal error: Cannot use `set` with `type = "%s"`', param_type))
elseif param_type == "number" then
-- Өзгөчө жагдайлар керек, анткени текшерүү сандарга өткөндөн кийин болот.
return val
end
local newval = param.set[val]
if newval == nil then
local list = {}
for k, v in pairs(param.set) do
if v == true then
insert(list, dump(k))
else
insert(list, ("%s (alias of %s)"):format(dump(k), dump(v)))
end
end
sort(list)
-- Эгер параметр талап кылынбаса, анда тизменин аягына "же бош" деп коюңуз, параметр чындыгында талап кылынат дегенди билдирбеш үчүн.
if not param.required then
insert(list, "бош")
end
convert_val_error(val, name, list)
end
if newval == true then
return val
end
return newval
end
local function convert_language(val, name, param, allow_etym)
local method, func = param.method
if method == nil or method == "code" then
func, method = get_language_by_code, "code"
elseif method == "name" then
func, method = get_language_by_name, "name"
else
error(format('Ички ката: "тил" түрү үчүн күтүлгөн "ыкма" "код", "аты" же аныкталбаган, бирок %s көрдү', dump(method)))
end
local lang = func(val, nil, allow_etym, param.family)
if lang then
return lang
end
local list, links = {"language"}, {"[[WT:LOL]]"}
if allow_etym then
insert(list, "etymology language")
insert(links, "[[WT:LOL/E]]")
end
if param.family then
insert(list, "family")
insert(links, "[[WT:LOF]]")
end
convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and "))
end
-- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment.
local type_handlers = setmetatable({
["boolean"] = function(val)
return yesno(val, true)
end,
["family"] = function(val, name, param)
local method, func = param.method
if method == nil or method == "code" then
func, method = get_family_by_code, "code"
elseif method == "name" then
func, method = get_family_by_name, "name"
else
error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method)))
end
return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]")
end,
["labels"] = function(val, name, param)
-- FIXME: Should be able to pass in a parse_err function.
return split_labels_on_comma(val)
end,
["language"] = function(val, name, param)
return convert_language(val, name, param, true)
end,
["full language"] = function(val, name, param)
return convert_language(val, name, param)
end,
["number"] = function(val, name, param)
local allow_hex = param.allow_hex
if allow_hex and allow_hex ~= true then
error(format('Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s', dump(allow_hex)))
end
local num = tonumber(val)
-- Avoid converting inputs like "nan" or "inf", and disallow 0x hex inputs unless explicitly enabled
-- with `allow_hex`.
if not (num and is_finite_real_number(num) and (allow_hex or not match(val, "^[+-]?0[Xx]%x*%.?%x*$"))) then
convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number")
-- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0).
elseif not is_positive_integer(num) then
track("number not a positive integer")
if num == 0 then
track("number is 0")
elseif not is_integer(num) then
track("number not an integer")
end
end
if param.set then
-- Don't pass in "number" here; otherwise no checking will happen.
num = check_set(num, name, param)
end
return num
end,
["parameter"] = function(val, name, param)
-- Use the `no_trim` option, as any trimming will have already been done.
return scribunto_param_key(val, true)
end,
["qualifier"] = function(val, name, param)
return {val}
end,
["references"] = function(val, name, param)
return parse_references(val, make_parse_err(val, name))
end,
["script"] = function(val, name, param)
local method, func = param.method
if method == nil or method == "code" then
func, method = get_script_by_code, "code"
elseif method == "name" then
func, method = get_script_by_name, "name"
else
error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method)))
end
return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]")
end,
["string"] = function(val, name, param) -- To be removed as unnecessary.
track("string")
return val
end,
-- TODO: add support for resolving to unsupported titles.
-- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive.
["title"] = function(val, name, param)
local namespace = param.namespace
if namespace == nil then
namespace = 0
else
local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or
not namespaces[namespace] and "a valid namespace number" or
nil
if valid_type then
error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace)))
end
end
-- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided).
local decoded, prefix, title = decode_entities(val), param.prefix
-- If the input is a fragment, treat the title as the current title with the input fragment.
if sub(decoded, 1, 1) == "#" then
-- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix.
if current_namespace == nil then
local current_title = mw_title.getCurrentTitle()
current_title_text, current_namespace = current_title.prefixedText, current_title.namespace
end
if not (prefix == "force" and namespace ~= current_namespace) then
title = new_title(current_title_text .. val)
end
elseif prefix == "force" then
-- Unconditionally add the namespace prefix (mw.title.makeTitle).
title = make_title(namespace, decoded)
elseif prefix == "full override" then
-- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix.
title = new_title(val, namespace)
elseif prefix == nil or prefix == "namespace override" then
-- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override).
local success
success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace)
-- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0.
if not success then
title = make_title(namespace, decoded)
end
else
error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix)))
end
local allow_external = param.allow_external
if allow_external == true then
return title or convert_val_error(val, name, "Wiktionary or external page title")
elseif not allow_external then
return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title")
end
error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external)))
end,
["Wikimedia language"] = function(val, name, param)
local fallback = param.fallback
if fallback == true then
return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code")
elseif not fallback then
return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code")
end
error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback)))
end,
}, {
-- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream functions decoding the output from this module, which would be double-decoding. Note that "title" has this implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by me.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the handler.
__call = function(self, val, name, param, param_type)
local val_type = type(val)
-- TODO: check this for all possible parameter types.
if val_type == param_type then
return val
-- TODO: throw an internal error.
elseif val_type ~= "string" then
track("input is not string")
track("input is not string/type handlers")
end
local func = self[param_type]
if func == nil then
error(format("Internal error: %s is not a recognized parameter type.", dump(param_type)))
end
return func(val, name, param)
end
})
--[==[ func: export.convert_val(val, name, param)
Өткөрүлгөн `params` таблицасында тизмеленген тиешелүү спецификацияларга ылайык параметрдин маанисин айландырыңыз
[[Модуль:parameters]]. `val` аты `name` (ката билдирүүлөрүндө гана колдонулат) болгон параметр үчүн конвертациялануучу маани.
`param` - спецификация (параметр үчүн `params` таблицасынын маани бөлүгү). Параметрдин аталышындагы өтүүнүн ордуна,
`name` ката кетирүүчү функция болушу мүмкүн, параметр аты жана мааниси менен бирге көрсөтүлгөн билдирүүнү көрсөтөт.
Бул функция 'param' ичиндеги бардык конверсияга байланыштуу талааларды иштетет, анын ичинде 'түр', 'коюу', 'кошумча тизме', 'конверттөө',
ж.б. Бул конверттелген маанини кайтарат.
]==]
local function convert_val(val, name, param)
local param_type = param.type or "string"
-- Эгер param.type функция болсо, аны таанылган түргө чечиңиз.
if is_callable(param_type) then
param_type = param_type(val)
end
local sublist = param.sublist
if sublist then
local retlist = {}
if type(val) ~= "string" then
error(format("Ички ката: %s сап эмес.", dump(val)))
end
if param.convert then
local thisval, insval
local thisindex = 0
local parse_err
if is_callable(name) then
-- Биз `name` ичинде өтүп кеткен ката функциясы параметрдин аталышын жана чийки маанини көрсөтүп турат деп ойлойбуз.
function parse_err(msg)
name(format("%s: пункт #%s=%s",
msg_with_processed(msg, thisval, insval), thisindex, thisval)
)
end
else
function parse_err(msg)
error(format("%s: пункт #%s=%s параметринин %s=%s",
msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val)
)
end
end
for v in split_sublist(val, name, sublist) do
thisval = v
thisindex = thisindex + 1
if param.set then
v = check_set(v, name, param, param_type)
end
insert(retlist, param.convert(type_handlers(v, name, param, param_type), parse_err))
end
else
for v in split_sublist(val, name, sublist) do
if param.set then
v = check_set(v, name, param, param_type)
end
insert(retlist, type_handlers(v, name, param, param_type))
end
end
return retlist
else
if param.set then
val = check_set(val, name, param, param_type)
end
local retval = type_handlers(val, name, param, param_type)
if param.convert then
local parse_err
if is_callable(name) then
-- Биз `name` ичинде өтүп кеткен ката функциясы параметрдин аталышын жана чийки маанини көрсөтүп турат деп ойлойбуз.
if retval == val then
-- Бул жабууну түзбөө үчүн оптималдаштыруу. Экинчи кол да туура иштейт
-- качан retval == val.
parse_err = name
else
function parse_err(msg)
name(msg_with_processed(msg, val, retval))
end
end
else
function parse_err(msg)
error(format("%s: параметрдин %s=%s", msg_with_processed(msg, val, retval), name, val))
end
end
retval = param.convert(retval, parse_err)
end
return retval
end
end
export.convert_val = convert_val -- used by [[Module:parameter utilities]]
local function unknown_param(name, val, args_unknown)
track("unknown parameters")
args_unknown[name] = val
return args_unknown
end
local function check_string_param_modifier(param_type, name, tag)
if param_type and not (param_type == "string" or param_type == "parameter" or type(param_type) == "function") then
internal_process_error(
"%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.",
tag, "type", "string", "параметрдин", name, param_type
)
end
end
local function hole_error(params, name, listname, this, nxt, extra)
-- `process_error` calls `dump` on values to be inserted into
-- error messages, but with numeric lists this causes "numeric"
-- to look like the name of the list rather than a description,
-- as `dump` adds quote marks. Insert it early to avoid this,
-- but add another %s specifier in all other cases, so that
-- actual list names will be displayed properly.
local offset, specifier, starting_from = 0, "%s", ""
local msg = "%s параметрлеринин тизмесиндеги %%d пункту берилиши керек, эгерде %%d пункту берилсе, анткени %s параметрлери жетишпей калгандыктан, % анда эч кандай боштук болбошу керек."
local specs = {}
if type(listname) == "string" then
specs[2] = listname
elseif type(name) == "number" then
offset = name - 1 -- To get the original parameter.
specifier = "numeric"
-- If the list doesn't start at parameter 1, avoid implying
-- there can't be any gaps in the numeric parameters if
-- some parameter with a lower key is optional.
for j = name - 1, 1, -1 do
local _param = params[j]
if not (_param and _param.required) then
starting_from = format("(starting from parameter %d) ", dump(j + 1))
break
end
end
else
specs[2] = name
end
specs[1] = this + offset -- Absolute index for this item.
insert(specs, nxt + offset) -- Absolute index for the next item.
process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs))
end
local function check_disallow_holes(params, val, name, listname, extra)
for i = 1, val.maxindex do
if val[i] == nil then
hole_error(params, name, listname, i, num_keys(val)[i], extra)
end
end
end
local function handle_holes(params, val, name)
local param = params[name]
local disallow_holes = param.disallow_holes
-- Iterate up the list, and throw an error if a hole is found.
if disallow_holes then
check_disallow_holes(params, val, name, param.list, " or empty")
end
-- Iterate up the list, and throw an error if a hole is found due to a
-- missing parameter, treating empty parameters as part of the list. This
-- applies beyond maxindex if blank arguments are supplied beyond it, so
-- isn't mutually exclusive with `disallow_holes`.
local empty = val.empty
if param.disallow_missing then
if empty then
-- Remove `empty` from `val`, so it doesn't get returned.
val.empty = nil
for i = 1, max(val.maxindex, maxn(empty)) do
if val[i] == nil and not empty[i] then
local keys = extend(num_keys(val), num_keys(empty))
sort(keys)
hole_error(params, name, param.list, i, keys[i])
end
end
-- If there's no table of empty parameters, the check is identical to
-- `disallow_holes`, except that the error message only refers to
-- missing parameters, not missing or empty ones. If `disallow_holes` is
-- also set, there's no point checking again.
elseif not disallow_holes then
check_disallow_holes(params, val, name, param.list)
end
end
-- If `allow_holes` is set, there's nothing left to do.
if param.allow_holes then
return
-- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and
-- iterating from 1 to `maxindex` times out with inputs like |100000000000=,
-- so use num_keys to get a list of numerical keys sorted from lowest to
-- highest, then iterate up the list, moving each value in `val` to the
-- lowest unused positive integer key. This also avoids the need to create a
-- new table. If `disallow_holes` is specified, then there can't be any
-- holes in the list, so there's no reason to check again; this doesn't
-- apply to `disallow_missing`, however.
elseif not disallow_holes then
local keys, i = num_keys(val), 0
while true do
i = i + 1
local key = keys[i]
if key == nil then
break
elseif i ~= key then
val[i], val[key] = val[key], nil
end
end
end
-- Some code depends on only numeric params being present when no holes are
-- allowed (e.g. by checking for the presence of arguments using next()), so
-- remove `maxindex`.
val.maxindex = nil
end
-- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or
-- module page. This means a different default can be specified for the template or module page example. However,
-- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages
-- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the
-- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard.
local function convert_default_val(name, param, pagename_set, any_args_set)
if not pagename_set then
local val = param.template_default
if val ~= nil and not any_args_set and is_own_page() then
return convert_val(val, name, param)
end
end
local val = param.default
if val ~= nil then
return convert_val(val, name, param)
end
end
local function is_string_or_number(item)
return type(item) == "string" or type(item) == "number"
end
local function list_to_alias_map(list)
local set, i = {}, 0
while true do
i = i + 1
local item = list[i]
if item == nil then
return set
end
if type(item) == "table" then
local canon = item[1]
if not is_string_or_number(canon) then
internal_process_error("First element of list item #%s must be a string or number: %s", i, canon)
end
set[canon] = true
local j = 1
while true do
j = j + 1
local alias = item[j]
if alias == nil then
break
end
if not is_string_or_number(alias) then
internal_process_error("Alias at position %s of canonical item %s at position %s must be a string or number: %s",
j, canon, i, alias)
end
set[alias] = canon
end
else
if not is_string_or_number(item) then
internal_process_error("Alias map element #%s must be a string or number: %s", i, item)
end
set[item] = true
end
end
return set
end
--[==[
Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args`
parameter specifies the arguments to be processed; they are the arguments you might retrieve from
{frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params`
parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in
the parameter table, an error is thrown.
The structure of the `params` table is as described above in the intro comment.
'''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the
same value objects in memory without causing problems.
The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an
argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the
processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This
allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at
once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of
the remainder to the function that handles a specific inflectional type.
]==]
function export.process(args, params, return_unknown)
-- Process parameters for specific properties
local args_new, args_unknown, any_args_set, spec_types, required, patterns, list_args, index_list, args_placeholders, placeholders_n = {}
-- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for many parameter names.
for name, param in pairs(params) do
validate_name(name, "parameter names")
if spec_types == nil then
spec_types = {}
end
local param_spec_type = type(param)
spec_types[param] = param_spec_type
if param_spec_type == "table" then
-- Populate required table, and make sure aliases aren't set to required.
if param.required then
if param.alias_of then
internal_process_error(
"Parameter %s is an alias of %s, but is also set as a required parameter. Only %s should be set as required.",
name, param.alias_of, name
)
elseif required == nil then
required = {}
end
required[name] = true
end
-- FIXME: modifying one of the input tables is a bad idea.
-- Convert param.set from a list into a set.
-- `converted_set` prevents double-conversion if multiple parameter keys share the same param table.
-- rawset avoids errors if param has been loaded via mw.loadData; however, it's probably more efficient to preconvert them, and set the `converted_set` key in advance.
local set = param.set
if set and not param.converted_set then
rawset(param, "set", list_to_alias_map(set))
rawset(param, "converted_set", true)
end
local listname, alias = param.list, param.alias_of
if alias then
validate_name(alias, "the alias_of field of parameter ", name)
-- Check that the alias_of is set to a valid parameter.
if not params[alias] then
internal_process_error(
"Parameter %s is an alias of an invalid parameter.",
name
)
elseif alias == name then
internal_process_error(
"Parameter %s cannot be an alias of itself.",
name
)
end
local main_param = params[alias]
local main_spec_type = spec_types[main_param] or type(main_param) -- Might not yet be memoized.
-- Aliases can't be lists unless the canonical parameter is also a list.
if listname and not (main_spec_type == "table" and main_param.list) then
internal_process_error(
"The list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias
)
-- Can't be an alias of an alias.
elseif main_spec_type == "table" then
local main_alias_of = main_param.alias_of
if main_alias_of ~= nil then
internal_process_error(
"alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.",
name, alias, main_alias_of, name, main_alias_of
)
end
end
end
if listname then
if not alias then
local key = name
if type(name) == "string" then
key = gsub(name, "\1", "")
end
local list_arg = {maxindex = 0}
args_new[key] = list_arg
if list_args == nil then
list_args = {}
end
list_args[key] = list_arg
end
local list_type = type(listname)
if list_type == "string" then
-- If the list property is a string, then it represents the name
-- to be used as the prefix for list items. This is for use with lists
-- where the first item is a numbered parameter and the
-- subsequent ones are named, such as 1, pl2, pl3.
patterns = save_pattern(name, listname, patterns or {})
elseif listname ~= true then
internal_process_error(
"The list field for parameter %s must be a boolean, string or undefined, but saw a %s.",
name, list_type
)
elseif type(name) == "number" then
if index_list ~= nil then
internal_process_error(
"Only one numeric parameter can be a list, unless the list property is a string."
)
end
-- If the name is a number, then all indexed parameters from
-- this number onwards go in the list.
index_list = name
else
patterns = save_pattern(name, name, patterns or {})
end
if find(name, "\1", nil, true) then
if args_placeholders then
placeholders_n = placeholders_n + 1
args_placeholders[placeholders_n] = name
else
args_placeholders, placeholders_n = {name}, 1
end
end
end
elseif param ~= true then
internal_process_error(
"Spec for parameter %s must be a table of specs or the value true, but found %s.",
name, param_spec_type ~= "boolean" and param_spec_type or param
)
end
end
--Process required changes to `params`.
if args_placeholders then
for i = 1, placeholders_n do
local name = args_placeholders[i]
params[gsub(name, "\1", "")], params[name] = params[name], nil
end
end
-- Process the arguments
for name, val in pairs(args) do
any_args_set = true
validate_name(name, "argument names", nil, true)
-- Once all of these have been eliminated, throw an internal error.
-- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions.
if type(val) ~= "string" then
track("input is not string")
track("input is not string/raw")
end
local orig_name, raw_type, index, canonical = name, type(name)
if raw_type == "number" then
if index_list and name >= index_list then
index = name - index_list + 1
name = index_list
end
elseif patterns then
-- Does this argument name match a pattern?
for pattern, pname in next, patterns do
index = match(name, pattern)
-- It matches, so store the parameter name and the
-- numeric index extracted from the argument name.
if index then
index = tonumber(index)
name = pname
break
end
end
end
local param = params[name]
-- If the argument is not in the list of parameters, store it in a separate list.
if not param then
args_unknown = unknown_param(name, val, args_unknown or {})
elseif param == true then
canonical = orig_name
val = php_trim(val)
if val ~= "" then
-- If the parameter is duplicated, throw an error.
if args_new[name] ~= nil then
process_error(
"Parameter %s has been entered more than once. This is probably because a parameter alias has been used.",
canonical
)
end
args_new[name] = val
end
else
if param.require_index then
-- Disallow require_index for numeric parameter names, as this doesn't make sense.
if raw_type == "number" then
internal_process_error(
"Cannot set require_index for numeric parameter %s.",
name
)
-- If a parameter without the trailing index was found, and
-- require_index is set on the param, treat it
-- as if it isn't recognized.
elseif not index then
args_unknown = unknown_param(name, val, args_unknown or {})
end
end
-- Check that separate_no_index is not being used with a numeric parameter.
if param.separate_no_index then
if raw_type == "number" then
internal_process_error(
"Cannot set separate_no_index for numeric parameter %s.",
name
)
elseif type(param.alias_of) == "number" then
internal_process_error(
"Cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.",
name, param.alias_of
)
end
end
-- If no index was found, use 1 as the default index.
-- This makes list parameters like g, g2, g3 put g at index 1.
-- If `separate_no_index` is set, then use 0 as the default instead.
if not index and param.list then
index = param.separate_no_index and 0 or 1
end
-- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index.
local raw_name = param.alias_of
if raw_name then
raw_type = type(raw_name)
if raw_type == "number" then
name = raw_name
local main_param = params[raw_name]
if spec_types[main_param] == "table" and main_param.list then
if not index then
index = param.separate_no_index and 0 or 1
end
canonical = raw_name + index - 1
else
canonical = raw_name
end
else
name = gsub(raw_name, "\1", "")
local main_param = params[name]
if not index and spec_types and spec_types[main_param] == "table" and main_param.list then
index = param.separate_no_index and 0 or 1
end
if not index or index == 0 then
canonical = name
elseif name == raw_name then
canonical = name .. index
else
canonical = gsub(raw_name, "\1", index)
end
end
else
canonical = orig_name
end
-- Only recognize demo parameters if this is the current template or module's
-- page, or its documentation page.
if param.demo and not is_own_page("include_documentation") then
args_unknown = unknown_param(name, val, args_unknown or {})
end
-- Remove leading and trailing whitespace unless no_trim is true.
if param.no_trim then
check_string_param_modifier(param.type, name, "no_trim")
else
val = php_trim(val)
end
-- Empty string is equivalent to nil unless allow_empty is true.
if param.allow_empty then
check_string_param_modifier(param.type, name, "allow_empty")
elseif val == "" then
-- If `disallow_missing` is set, keep track of empty parameters
-- via the `empty` field in `arg`, which will be used by the
-- `disallow_missing` check. This will be deleted before
-- returning.
if index and param.disallow_missing then
local arg = args_new[name]
local empty = arg.empty
if empty == nil then
empty = {}
arg.empty = empty
end
empty[index] = true
end
val = nil
end
-- Allow boolean false.
if val ~= nil then
-- Convert to proper type if necessary.
local main_param = params[raw_name]
if not main_param or (spec_types and spec_types[main_param] == "table") then
val = convert_val(val, orig_name, main_param or param)
end
-- Mark it as no longer required, as it is present.
if required then
required[name] = nil
end
-- Store the argument value.
if index then
local arg = args_new[name]
-- If the parameter is duplicated, throw an error.
if arg[index] ~= nil then
process_error(
"Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.",
canonical
)
end
arg[index] = val
-- Store the highest index we find.
local maxindex = max(index, arg.maxindex)
if arg[0] ~= nil then
arg.default, arg[0] = arg[0], nil
if maxindex == 0 then
maxindex = 1
end
end
arg.maxindex = maxindex
if not params[name].list then
args_new[name] = val
-- Don't store index 0, as it's a proxy for the default.
elseif index > 0 then
arg[index] = val
end
else
-- If the parameter is duplicated, throw an error.
if args_new[name] ~= nil then
process_error(
"Parameter %s has been entered more than once. This is probably because a parameter alias has been used.",
canonical
)
end
if not raw_name then
args_new[name] = val
else
local main_param = params[raw_name]
if spec_types[main_param] == "table" and main_param.list then
local main_arg = args_new[raw_name]
main_arg[1] = val
-- Store the highest index we find.
main_arg.maxindex = max(1, main_arg.maxindex)
else
args_new[raw_name] = val
end
end
end
end
end
end
-- Remove holes in any list parameters if needed. This must be handled
-- straight after the previous loop, as any instances of `empty` need to be
-- converted to nil.
if list_args then
for name, val in next, list_args do
handle_holes(params, val, name)
end
end
-- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it
-- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set,
-- which gets used in such cases as a demo.
-- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage),
-- or if the #invoke: is on a page in another namespace.
local pagename_set = args_new.pagename
-- Handle defaults.
for name, param in pairs(params) do
if spec_types[param] == "table" then
local arg_new = args_new[name]
if arg_new == nil then
args_new[name] = convert_default_val(name, param, pagename_set, any_args_set)
elseif param.list and arg_new[1] == nil then
local default_val = convert_default_val(name, param, pagename_set, any_args_set)
if default_val ~= nil then
arg_new[1] = default_val
if arg_new.maxindex == 0 then
arg_new.maxindex = 1
end
end
end
end
end
-- The required table should now be empty.
-- If any parameters remain, throw an error, unless we're on the current template or module's page.
if required and next(required) ~= nil and not is_own_page() then
params_list_error(required, "required")
-- Return the arguments table.
-- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value.
elseif return_unknown then
return args_new, args_unknown or {}
elseif args_unknown and next(args_unknown) ~= nil then
params_list_error(args_unknown, "not used by this template")
end
return args_new
end
return export