Difference between revisions of "Module:Road data/parser"

From blackwiki
Jump to navigation Jump to search
blackwiki>Fredddie
(sync to main)
blackwiki>Fredddie
(Reverted to revision 721024695 by Imzadi1979 (talk): Reverting to the before times (TW))
Line 1: Line 1:
local p = {} -- Package to be exported
+
-- Export package
 
+
local p = {}
-- Change to "" upon deployment.
 
local moduleSuffix = ""
 
 
 
local parserHooksModuleName = "Module:Road data/parser/hooks" .. moduleSuffix
 
  
 
-- Local library aliases
 
-- 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
  
---
+
-- Regex substitution patterns
-- Substitution pattern based on passed arguments
+
local prepattern = "%[(%w+)%|(.*)%|(.*)%|(.*)%]" -- [arg|equal to (blank for existence)|if true|if false]
-- Syntax: [param|value|match|mismatch]
+
local pattern = "%%(%w+)%%" -- %arg%
-- 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 function parser(formatStr, args, form)
-- Perform substitutions.
+
local function ifexists(name)
-- @param #string formatStr The string the be substituted
+
-- Check whether a page or file named name exists
-- @param #table args The arguments passed to this module
+
if name == '' then return false end -- Page doesn't exist if name is blank
local function subst(formatStr, args)
+
local title -- mw.title object
---
+
if form == 'shield' then
-- Perform a substitution based on passed argument.
+
title = mw.title.new(name, 'Media') -- Shields are in the Media namespace
-- @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
+
title = mw.title.new(name, 0) -- Links are in the mainspace
 
end
 
end
 +
return title.exists -- A boolean for whether the page exists
 
end
 
end
-- argument-test substitutions
+
local function testArgs(test, equals, ifexists, ifnot)
local preprocessed = gsub(formatStr, prepattern, testArgs)
+
-- Implement the if test
-- parameter substitutions
+
if equals ~= '' then -- Argument equals something
return (gsub(preprocessed, pattern, args))
+
if args[test] == equals then return ifexists else return ifnot end
-- gsub returns number of matches as second value.
+
else -- Existence of argument
-- The enclosing parens discards it.
+
if args[test] and args[test] ~= '' then return ifexists else return ifnot end
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
 
end
return true
 
 
end
 
end
-- Otherwise, `value` is a string, so check the existence for that string.
+
local formatTable = {} -- Table with definitions
return titleExists(value, key)
+
-- Recursively dig into tables that could be parser hooks or argument tables.
end
+
local function formatStrInTable(formatStr)
 
+
if type(formatStr) ~= "table" then return formatStr end -- formatStr is a scalar
---
+
formatTable = formatStr
-- Perform a translation on a given entry.
+
local hook = formatStr.hook -- Possible hook
-- @param entry An entry to be translated; may be any non-function type.
+
local both = formatStr[2] -- Second shield
-- A table may be a parser hook specification, a switch table, or an
+
if both then
-- ordinary value table.  Translations are applied recursively.
+
local first = formatStrInTable(formatStr[1])
-- @param #table args The arguments passed to this module
+
local second = formatStrInTable(both)
-- @param #string key The name of the entry being translated.
+
return {first, second} -- First and second shield
-- @return The translated entry
+
elseif hook then
local function translate(entry, args, key)
+
local hooksModule = require "Module:Road data/parser/hooks"
if type(entry) == "string" then
+
local hookFunction = hooksModule[hook] or error("Hook '" .. hook .. "' does not exist", 0)
return subst(entry, args) -- Substitute arguments as necessary.
+
return formatStrInTable(hookFunction(formatStr, args)) -- Call hook
elseif type(entry) ~= "table" then
+
else -- Switch on an argument
return entry
+
local arg = args[formatStr.arg or "route"]
elseif entry.hook then
+
return formatStrInTable(formatStr[arg] or formatStr.default)
-- 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
 
end
-- default result for mismatch
+
end
local defaultValue = translate(entry.default, args, key)
+
local function parse(formatStr)
if defaultValue and entry.ifexists then
+
local preprocessed = gsub(formatStr, prepattern, testArgs) -- If statements
-- Check existence.
+
local parsedStr = gsub(preprocessed, pattern, args) -- Variable interpolation
if ifexists(defaultValue, key) then return defaultValue end
+
local final = trim(parsedStr) -- Trim extra spaces
-- Failed existence check results in fallback value (default to nil).
+
if formatTable.ifexists then -- Existence test
return entry.otherwise and translate(entry.otherwise, args, key) or nil
+
local exists = ifexists(final)
else
+
if exists then
return defaultValue
+
return final
 +
else
 +
return parser(formatTable.otherwise, args, form)
 +
end
 
end
 
end
 +
return final
 +
end
 +
formatStr = formatStrInTable(formatStr) -- Get formatStr
 +
if not formatStr or formatStr == '' then return '' end -- Return empty string for empty formatStr
 +
if type(formatStr) == 'table' then -- Dual shields
 +
local first = parse(formatStr[1])
 +
local second = parse(formatStr[2])
 +
return first, second
 
else
 
else
-- This entry is a value table.
+
return parse(formatStr)
-- Process each table element.
 
local result = {}
 
for key,elem in pairs(entry) do
 
result[key] = translate(elem, args, key)
 
end
 
return result
 
 
end
 
end
 
end
 
end
  
---
+
local function formatString(args, form)
-- Retrieve an entry from a data module based on a given type and key.
+
-- Get format string/table from module based on jurisdiction and type
-- @param #string module The name of the data module to be fetched
+
local function getTypeData(module, type)
-- @param type The key for the type table within the loaded table
+
-- Get data from module for particular type
-- @param key The key for the entry within the type table
+
local success, moduleData = pcall(mw.loadData, module)
-- @return fetchedTable[type][key] if specified, where `fetchedTable` is the
+
if not success then return '' end -- Empty string if module cannot be loaded
-- table fetched from `module`, nil otherwise
+
local typeTable = moduleData[type] or moduleData[''] -- Type table or empty string default table
local function getTypeData(module, type, key)
+
local defaultTable = moduleData[''] or {} -- Default table
-- Attempt to fetch the given data module.
+
if typeTable then
local success, moduleData = pcall(mw.loadData, module)
+
local alias = typeTable.alias
if not success then return false, moduleData end -- Module could not be loaded
+
if alias then -- Type is an alias to another module
-- The type table defaults to empty-key table if undefined.
+
local aliasedModule = "Module:Road data/strings/" .. alias.module
local typeTable = moduleData[type] or moduleData['']
+
local aliasedType = alias.type
-- Fallback table is the empty-key table, with the empty table as default.
+
return getTypeData(aliasedModule, aliasedType)
local defaultTable = moduleData[''] or {}
+
end
if typeTable then
+
return typeTable[form] or defaultTable[form] or ''
local alias = typeTable.alias
+
else
if alias and alias.module and alias.type then
+
return ''
-- 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
 
end
return true, typeTable[key] or defaultTable[key] or nil
 
else
 
return true, nil
 
 
end
 
end
end
+
 
+
local stateCountries = {USA = true, CAN = true} -- These countries have state/province modules
---
 
-- 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 '')
 
local state = upper(args.state or '')
 
local country
 
local country
 
if args.country then
 
if args.country then
 
country = upper(args.country)
 
country = upper(args.country)
else
+
else -- Find country via a mask
-- Recover the country from the given state or province.
 
 
local countryModule = mw.loadData("Module:Road data/countrymask")
 
local countryModule = mw.loadData("Module:Road data/countrymask")
 
country = countryModule[state] or 'UNK'
 
country = countryModule[state] or 'UNK'
 
end
 
end
 +
local typeArg = args.type
 +
local module
 
if stateCountries[country] and state ~= '' then
 
if stateCountries[country] and state ~= '' then
-- Submodule within the country exists.
+
module = format("Module:Road data/strings/%s/%s", country, state)
return format("Module:Road data/strings/%s/%s", country, state)
+
else
 +
module = format("Module:Road data/strings/%s", country)
 
end
 
end
return format("Module:Road data/strings/%s", country)
+
return getTypeData(module, typeArg)
 
end
 
end
  
---
+
function p.parser(passedArgs, form)
-- 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)
-- @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
+
return parser(formatStr, args, form)
-- @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
 
end
  
 
return p
 
return p

Revision as of 13:00, 8 August 2019

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

-- Export package
local p = {}

-- Local library aliases
local format = string.format
local gsub = mw.ustring.gsub
local trim = mw.text.trim
local upper = mw.ustring.upper

-- Regex substitution patterns
local prepattern = "%[(%w+)%|(.*)%|(.*)%|(.*)%]" -- [arg|equal to (blank for existence)|if true|if false]
local pattern = "%%(%w+)%%" -- %arg%

local function parser(formatStr, args, form)
	local function ifexists(name)
		-- Check whether a page or file named name exists
		if name == '' then return false end -- Page doesn't exist if name is blank
		local title -- mw.title object
		if form == 'shield' then
			title = mw.title.new(name, 'Media') -- Shields are in the Media namespace
		else
			title = mw.title.new(name, 0) -- Links are in the mainspace
		end
		return title.exists -- A boolean for whether the page exists
	end
	local function testArgs(test, equals, ifexists, ifnot)
		-- Implement the if test
		if equals ~= '' then -- Argument equals something
			if args[test] == equals then return ifexists else return ifnot end
		else -- Existence of argument
			if args[test] and args[test] ~= '' then return ifexists else return ifnot end
		end
	end
	local formatTable = {} -- Table with definitions
	-- Recursively dig into tables that could be parser hooks or argument tables.
	local function formatStrInTable(formatStr)
		if type(formatStr) ~= "table" then return formatStr end -- formatStr is a scalar
		formatTable = formatStr
		local hook = formatStr.hook -- Possible hook
		local both = formatStr[2] -- Second shield
		if both then
			local first = formatStrInTable(formatStr[1])
			local second = formatStrInTable(both)
			return {first, second} -- First and second shield
		elseif hook then
			local hooksModule = require "Module:Road data/parser/hooks"
			local hookFunction = hooksModule[hook] or error("Hook '" .. hook .. "' does not exist", 0)
			return formatStrInTable(hookFunction(formatStr, args)) -- Call hook
		else -- Switch on an argument
			local arg = args[formatStr.arg or "route"]
			return formatStrInTable(formatStr[arg] or formatStr.default)
		end
	end
	local function parse(formatStr)
		local preprocessed = gsub(formatStr, prepattern, testArgs) -- If statements
		local parsedStr = gsub(preprocessed, pattern, args) -- Variable interpolation
		local final = trim(parsedStr) -- Trim extra spaces
		if formatTable.ifexists then -- Existence test
			local exists = ifexists(final)
			if exists then
				return final
			else
				return parser(formatTable.otherwise, args, form)
			end
		end
		return final
	end
	formatStr = formatStrInTable(formatStr) -- Get formatStr
	if not formatStr or formatStr == '' then return '' end -- Return empty string for empty formatStr
	if type(formatStr) == 'table' then -- Dual shields
		local first = parse(formatStr[1])
		local second = parse(formatStr[2])
		return first, second
	else
		return parse(formatStr)
	end
end

local function formatString(args, form)
	-- Get format string/table from module based on jurisdiction and type
	local function getTypeData(module, type)
		-- Get data from module for particular type
		local success, moduleData = pcall(mw.loadData, module)
		if not success then return '' end -- Empty string if module cannot be loaded
		local typeTable = moduleData[type] or moduleData[''] -- Type table or empty string default table
		local defaultTable = moduleData[''] or {} -- Default table
		if typeTable then
			local alias = typeTable.alias
			if alias then -- Type is an alias to another module
				local aliasedModule = "Module:Road data/strings/" .. alias.module
				local aliasedType = alias.type
				return getTypeData(aliasedModule, aliasedType)
			end
			return typeTable[form] or defaultTable[form] or ''
		else
			return ''
		end
	end
	
	local stateCountries = {USA = true, CAN = true} -- These countries have state/province modules
	local state = upper(args.state or '')
	local country
	if args.country then
		country = upper(args.country)
	else -- Find country via a mask
		local countryModule = mw.loadData("Module:Road data/countrymask")
		country = countryModule[state] or 'UNK'
	end
	local typeArg = args.type
	local module
	if stateCountries[country] and state ~= '' then
		module = format("Module:Road data/strings/%s/%s", country, state)
	else
		module = format("Module:Road data/strings/%s", country)
	end
	return getTypeData(module, typeArg)
end

function p.parser(passedArgs, form)
	local args = {state = passedArgs.state, type = passedArgs.type, route = passedArgs.route, 
	              denom = passedArgs.denom, county = passedArgs.county, dab = passedArgs.dab,
	              country = passedArgs.country, township = passedArgs.township}
	local formatStr = formatString(args, form)
	if not formatStr or formatStr == '' then return nil end
	return parser(formatStr, args, form)
end

return p