Jump to content

Module:Road data/parser: Difference between revisions

From Simple English Wikipedia, the free encyclopedia
Content deleted Content added
Happy5214 (talk | changes)
Directly determine and load proper module
m Protected "Module:Road data/parser": Highly visible template ([Edit=Allow only autoconfirmed users] (indefinite) [Move=Allow only autoconfirmed users] (indefinite))
 
(18 intermediate revisions by 5 users not shown)
Line 1: Line 1:
local p = {}
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 format = string.format
local gsub = mw.ustring.gsub
local gsub = mw.ustring.gsub
local trim = mw.text.trim
local upper = mw.ustring.upper
local upper = mw.ustring.upper


---
local prepattern = "%[(%w+)%|(.*)%|(.*)%|(.*)%]"
-- 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+)%%"
local pattern = "%%(%w+)%%"


---
local function parser(formatStr, args)
-- Perform substitutions.
local function testArgs(test, equals, ifexists, ifnot)
-- @param #string formatStr The string the be substituted
if equals ~= '' then
-- @param #table args The arguments passed to this module
if args[test] == equals then return ifexists else return ifnot end
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
else
return arg ~= '' and ifmatch or ifmismatch
if args[test] and args[test] ~= '' then return ifexists else return ifnot end
end
end
end
end
-- argument-test substitutions
-- Recursively dig into tables that could be parser hooks or argument tables.
local function formatStrInTable(formatStr)
local preprocessed = gsub(formatStr, prepattern, testArgs)
-- parameter substitutions
if type(formatStr) ~= "table" then return formatStr end
return (gsub(preprocessed, pattern, args))
local hook = formatStr.hook
-- gsub returns number of matches as second value.
if hook then
-- The enclosing parens discards it.
local hooksModule = require "Module:Road data/parser/hooks"
end
local hookFunction = hooksModule[hook] or error("Hook '" .. hook .. "' does not exist", 0)

return formatStrInTable(hookFunction(formatStr, args))
---
else
-- Determine whether a given title exists on Wikipedia.
local arg = args[formatStr.arg or "route"]
-- @param #string name The title, e.g., article name and file name,
return formatStrInTable(formatStr[arg] or formatStr.default)
-- 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
end
return true
end
end
-- Otherwise, `value` is a string, so check the existence for that string.
formatStr = formatStrInTable(formatStr)
return titleExists(value, key)
if not formatStr or formatStr == '' then return '' end
local preprocessed = gsub(formatStr, prepattern, testArgs)
local final = gsub(preprocessed, pattern, args)
return trim(final)
end
end


---
local function formatString(args, form, part)
-- Perform a translation on a given entry.
local stateCountries = {USA = true, CAN = true}
-- @param entry An entry to be translated; may be any non-function type.
local state = upper(args.state or '')
-- A table may be a parser hook specification, a switch table, or an
local country
-- ordinary value table. Translations are applied recursively.
if args.country then
-- @param #table args The arguments passed to this module
country = upper(args.country)
-- @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
else
-- This entry is a value table.
local countryModule = mw.loadData("Module:Road data/countrymask")
-- Process each table element.
country = countryModule[state] or 'UNK'
local result = {}
end
for key,elem in pairs(entry) do
local typeArg = args.type
result[key] = translate(elem, args, key)
local module
end
if stateCountries[country] then
return result
module = format("Module:Road data/strings/%s/%s", country, state)
else
module = format("Module:Road data/strings/%s", country)
end
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)
local success, moduleData = pcall(mw.loadData, module)
if not success then return '' end
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[typeArg]
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
if typeTable then
return part and typeTable[form][part] or typeTable[form]
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
else
return ''
return true, nil
end
end
end
end


---
local function ifexists(name, form)
-- Determine the module name for the lookup by country and state.
local title
-- @param #table args The arguments passed to this module
if form == 'shield' then
-- @return #string The module name to be fetched
title = mw.title.new(name, 'Media')
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
else
-- Recover the country from the given state or province.
title = mw.title.new(name, 0)
local countryModule = mw.loadData("Module:Road data/countrymask")
country = countryModule[state] or 'UNK'
end
end
if stateCountries[country] and state ~= '' then
if title.exists then
-- Submodule within the country exists.
return name
return format("Module:Road data/strings/%s/%s", country, state)
else
return ''
end
end
return format("Module:Road data/strings/%s", country)
end
end


---
function p.parser(passedArgs, form, part)
-- Fetch the entry from the appropriate module, and return that entry
local args = {state = passedArgs.state, type = passedArgs.type, route = passedArgs.route,
-- substituted with appropriate values.
denom = passedArgs.denom, county = passedArgs.county, dab = passedArgs.dab,
-- @param #table args The arguments to be used for lookup and substitutions
country = passedArgs.country, township = passedArgs.township}
-- @param #string key The key for the entry within the type table
local formatStr = formatString(args, form, part)
-- @param #string type (optional) The key for the type table within the fetched
if not formatStr or formatStr == '' then return nil end
-- module; defaults to args.type
local parsedStr = parser(formatStr, args)
-- @param #string moduleName (optional) The name of the module to be fetched;
if type(formatStr) == 'table' and formatStr.ifexists then
-- defaults to the module determined by country and state
return ifexists(parsedStr, form)
-- @return The substituted entry
else
function p.parser(args, key, type, moduleName)
return parsedStr
-- Determine module name, if not given.
end
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
end



Latest revision as of 19:26, 10 January 2025

Documentation for this module may be created at Module:Road data/parser/doc

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