if param > 0 then
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end  
root = {} -- array
root.req = {}
cur = newObject(root)
prev = nil  
esc = false
param = 0  
for i = 1, #str do
chr = str:sub(i,i)  
if not esc then
if chr == '\\' then
endParam
esc = true
elseif chr == '%' then
endParam
if cur.str ~= "" then
cur = newObject(cur.parent)
end
param = 2
elseif chr == '[' then
endParam
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {} -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam
end
end  
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
cur.str = cur.str .. chr
esc = false
end  
prev = nil
end  
endParam  
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end  
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end  
return root, params
end  
local function sortOnRank(claims)
local rankPos
local ranks = -- preferred, normal, deprecated, (default)
local sorted = {}  
for _, v in ipairs(claims) do
rankPos = rankTable[v.rank] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end  
sorted = ranks[1]
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])  
return sorted
end  
local Config = {}  
-- allows for recursive calls
function Config:new
local cfg = {}
setmetatable(cfg, self)
self.__index = self  
cfg.separators = {
-- single value objects wrapped in arrays so that we can pass by reference
["sep"] = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"] = {copyTable(defaultSeparators["punc"])}
}  
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}  
cfg.bestRank = true
cfg.ranks = {true, true, false} -- preferred = true, normal = true, deprecated = false
cfg.foundRank = #cfg.ranks
cfg.flagBest = false
cfg.flagRank = false  
cfg.periods = {true, true, true} -- future = true, current = true, former = true
cfg.flagPeriod = false
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))} -- today as {year, month, day}  
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false  
cfg.inSitelinks = false  
cfg.langCode = mw.language.getContentLanguage.code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)  
cfg.siteID = mw.wikibase.getGlobalSiteId  
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil  
cfg.prefetchedRefs = nil  
return cfg
end  
local State = {}  
function State:new(cfg, type)
local stt = {}
setmetatable(stt, self)
self.__index = self  
stt.conf = cfg
stt.type = type  
stt.results = {}  
stt.parsedFormat = {}
stt.separator = {}
stt.movSeparator = {}
stt.puncMark = {}  
stt.linked = false
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false  
return stt
end  
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local prefix, title= "", nil  
if not id then
id = mw.wikibase.getEntityIdForCurrentPage  
if not id then
return ""
end
end  
id = id:upper -- just to be sure  
if raw then
-- check if given id actually exists
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
end  
prefix, title = "d:Special:EntityPage/", label -- may be nil
else
-- try short name first if requested
if short then
label = p._property{aliasesP.shortName, [p.args.eid] = id} -- get short name  
if label == "" then
label = nil
end
end  
-- get label
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end  
if not label then
label = ""
elseif link then
-- build a link if requested
if not title then
if id:sub(1,1) == "Q" then
title = mw.wikibase.getSitelink(id)
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
prefix, title = "d:Special:EntityPage/", id
end
end  
label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
label = buildWikilink(prefix .. title, label)
end
end  
return label
end  
function Config:getEditIcon
local value = ""
local prefix = ""
local front = "&nbsp;"
local back = ""  
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end  
if self.editAtEnd then
front = '<span>'
back = '</span>'
end  
value = ""  
return front .. value .. back
end  
-- used to create the final output string when it's all done, so that for references the
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip  
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false  
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end  
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame:extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end  
return outString
end  
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
local itemID  
if unit == "" or unit == "1" then
return nil
end  
if unitOnly then
space = ""
end  
itemID = parseWikidataURL(unit)  
if itemID then
if itemID == aliasesQ.percentage then
return "%"
else
label = self:getLabel(itemID, raw, link, short)  
if label ~= "" then
return space .. label
end
end
end  
return ""
end  
function State:getValue(snak)
return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
end  
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
if snak.snaktype == 'value' then
local datatype = snak.datavalue.type
local subtype = snak.datatype
local datavalue = snak.datavalue.value  
if datatype == 'string' then
if subtype == 'url' and link then
-- create link explicitly
if raw then
-- will render as a linked number like [1]
return "[" .. datavalue .. "]"
else
return "[" .. datavalue .. " " .. datavalue .. "]"
end
elseif subtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return ""
else
return datavalue
end
elseif subtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
local attribute = nil  
if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
attribute = {qid = self.entityID}
end  
return mw.getCurrentFrame:extensionTag("math", datavalue, attribute)
elseif subtype == 'external-id' and link then
local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property} -- get formatter URL  
if url ~= "" then
url = mw.ustring.gsub(url, "$1", datavalue)