local p = {} -- Package to be exported  -- Change to "" upon deployment. local moduleSuffix = ""  local parserHooksModuleName = "Module:Road data/parser/hooks" .. moduleSuffix  -- Local library aliases local format = string.format local gsub = mw.ustring.gsub local upper = mw.ustring.upper  --- -- Substitution pattern based on passed arguments -- Syntax: [param|value|match|mismatch] -- where --  param is the parameter name to be tested --  value is the value to test against argument; if empty, the argument is --    tested for existence --  match is the string to be substituted if the argument matches value --  mismatch is the string to be substituted if the argument does not match --    the value -- These arguments may not contain "[", "|", or "]". local prepattern = "%[(%w+)%|([^%[|%]]*)%|([^%[|%]]*)%|([^%[|%]]*)%]" --- -- Parameter substitution pattern -- Syntax: %param% -- where param is the name of the parameter whose value is to be substituted -- in place of %param%. local pattern = "%%(%w+)%%"  --- -- Perform substitutions. -- @param #string formatStr The string the be substituted -- @param #table args The arguments passed to this module local function subst(formatStr, args) 	--- 	-- Perform a substitution based on passed argument. 	-- @param #string param The parameter name to be tested 	-- @param #string value The value to test against argument; if empty, 	-- 		the argument is tested for existence 	-- @param #string ifmatch The resulting string if the argument matches 	-- 		`value` 	-- @param #string ifmismatch The resulting string if the argument does not 	-- 		match `value` 	-- @return #string either `ifmatch` or `ifmismatch`, based on the test 	local function testArgs(param, value, ifmatch, ifmismatch) 		local arg = args[param] or 		if value ~=  then 			return arg == value and ifmatch or ifmismatch 		else 			return arg ~=  and ifmatch or ifmismatch 		end 	end 	-- argument-test substitutions 	local preprocessed = gsub(formatStr, prepattern, testArgs) 	-- parameter substitutions 	return (gsub(preprocessed, pattern, args)) 	-- gsub returns number of matches as second value. 	-- The enclosing parens discards it. end  --- -- Determine whether a given title exists on Wikipedia. -- @param #string name The title, e.g., article name and file name, -- 		without namespace prefix -- @param #string key The name of the entry being translated. -- @return #boolean `true` if the title exists, false otherwise local function titleExists(name, key) 	if name ==  then return false end 	local namespaceModule = mw.loadData('Module:Road data/parser/namespace') 	-- Retrieve the namespace for `key`. 	local namespace = namespaceModule[key] or 0 	local title = mw.title.new(name, namespace); 	return title.exists end  --- -- Determine whether titles exist on Wikipedia. -- @param value A string or a table containing strings of titles to be checked -- 		against -- @param #string key The name of the entry being translated. -- @return #boolean `true` if all titles exist, false otherwise local function ifexists(value, key) 	local valueType = type(value) 	if valueType == "table" then 		-- If `value` is a table, recursively check the existence 		-- for each element within the table. 		for _,entry in pairs(value) do 			if not ifexists(entry, key) then return false end 		end 		return true 	end 	-- Otherwise, `value` is a string, so check the existence for that string. 	return titleExists(value, key) end  --- -- Perform a translation on a given entry. -- @param entry An entry to be translated; may be any non-function type. -- 		A table may be a parser hook specification, a switch table, or an -- 		ordinary value table.  Translations are applied recursively. -- @param #table args The arguments passed to this module -- @param #string key The name of the entry being translated. -- @return The translated entry local function translate(entry, args, key) 	if type(entry) == "string" then 		return subst(entry, args) -- Substitute arguments as necessary. 	elseif type(entry) ~= "table" then 		return entry 	elseif entry.hook then 		-- This entry is a parser hook. 		-- Requires: Parser hook must have hook field. 		local hook = entry.hook 		local parserHooksModule = require(parserHooksModuleName) 		local hookFunction = parserHooksModule[hook] 			or error("Hook '" .. hook .. "' does not exist", 0) 		return translate(hookFunction(entry, args), args, key) 	elseif entry.arg or entry.undefined or entry.default then 		-- This entry is a switch table. 		-- Requires: Switch table must have 		--           arg, undefined, or default fields 		--           but not hook field. 		local arg = args[entry.arg or "route"] 		if entry[arg] then return translate(entry[arg], args, key) end 		if arg == nil and entry.undefined ~= nil then 			-- result for unspecified argument 			return translate(entry.undefined, args, key) 		end 		-- default result for mismatch 		local defaultValue = translate(entry.default, args, key) 		if defaultValue and entry.ifexists then 			-- Check existence. 			if ifexists(defaultValue, key) then return defaultValue end 			-- Failed existence check results in fallback value (default to nil). 			return entry.otherwise and translate(entry.otherwise, args, key) or nil 		else 			return defaultValue 		end 	else 		-- This entry is a value table. 		-- Process each table element. 		local result = {} 		for key,elem in pairs(entry) do 			result[key] = translate(elem, args, key) 		end 		return result 	end end  --- -- Retrieve an entry from a data module based on a given type and key. -- @param #string module The name of the data module to be fetched -- @param type The key for the type table within the loaded table -- @param key The key for the entry within the type table -- @return fetchedTable[type][key] if specified, where `fetchedTable` is the -- 		table fetched from `module`, nil otherwise local function getTypeData(module, type, key) 	-- Attempt to fetch the given data module. 	local success, moduleData = pcall(mw.loadData, module) 	if not success then return false, moduleData end -- Module could not be loaded 	-- The type table defaults to empty-key table if undefined. 	local typeTable = moduleData[type] or moduleData[] 	-- Fallback table is the empty-key table, with the empty table as default. 	local defaultTable = moduleData[] or {} 	if typeTable then 		local alias = typeTable.alias 		if alias and alias.module and alias.type then 			-- The type table is an alias table. 			-- Recursively fetch the aliased type data. 			local aliasedModule = "Module:Road data/strings/" .. alias.module 			local aliasedType = alias.type 			return getTypeData(aliasedModule, aliasedType, key) 		end 		return true, typeTable[key] or defaultTable[key] or nil 	else 		return true, nil 	end end  --- -- Determine the module name for the lookup by country and state. -- @param #table args The arguments passed to this module -- @return #string The module name to be fetched local function getModuleName(args) 	-- countries with submodules for states or provinces 	local stateCountries = {USA = true, CAN = true} 	local state = upper(args.state or args.province or ) 	local country 	if args.country then 		country = upper(args.country) 	else 		-- Recover the country from the given state or province. 		local countryModule = mw.loadData("Module:Road data/countrymask") 		country = countryModule[state] or 'UNK' 	end 	if stateCountries[country] and state ~=  then 		-- Submodule within the country exists. 		return format("Module:Road data/strings/%s/%s", country, state) 	end 	return format("Module:Road data/strings/%s", country) end  --- -- Fetch the entry from the appropriate module, and return that entry -- substituted with appropriate values. -- @param #table args The arguments to be used for lookup and substitutions -- @param #string key The key for the entry within the type table -- @param #string type (optional) The key for the type table within the fetched -- 		module; defaults to args.type -- @param #string moduleName (optional) The name of the module to be fetched; -- 		defaults to the module determined by country and state -- @return The substituted entry function p.parser(args, key, type, moduleName) 	-- Determine module name, if not given. 	local dataModuleName = moduleName or getModuleName(args) 	-- Fetch the entry from the module. 	local success, formatStr = getTypeData(dataModuleName, type or args.type, key) 	if not success then return false, formatStr end 	-- Translate the entry. 	return translate(formatStr, args, key) end  return p