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

From blackwiki
Jump to navigation Jump to search
blackwiki>Happy5214
(August update: Add multiple shield support; use empty string type as fallback for non-existent types)
blackwiki>Happy5214
m (Comments)
Line 1: Line 1:
 +
-- Export package
 
local p = {}
 
local p = {}
 +
 +
-- Local library aliases
 
local format = string.format
 
local format = string.format
 
local gsub = mw.ustring.gsub
 
local gsub = mw.ustring.gsub
Line 5: Line 8:
 
local upper = mw.ustring.upper
 
local upper = mw.ustring.upper
  
local prepattern = "%[(%w+)%|(.*)%|(.*)%|(.*)%]"
+
-- Regex substitution patterns
local pattern = "%%(%w+)%%"
+
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 parser(formatStr, args, form)
 
local function ifexists(name)
 
local function ifexists(name)
if name == '' then return false end
+
-- Check whether a page or file named name exists
local title
+
if name == '' then return false end -- Page doesn't exist if name is blank
 +
local title -- mw.title object
 
if form == 'shield' then
 
if form == 'shield' then
title = mw.title.new(name, 'Media')
+
title = mw.title.new(name, 'Media') -- Shields are in the Media namespace
 
else
 
else
title = mw.title.new(name, 0)
+
title = mw.title.new(name, 0) -- Links are in the mainspace
 
end
 
end
return title.exists
+
return title.exists -- A boolean for whether the page exists
 
end
 
end
 
local function testArgs(test, equals, ifexists, ifnot)
 
local function testArgs(test, equals, ifexists, ifnot)
if equals ~= '' then
+
-- Implement the if test
 +
if equals ~= '' then -- Argument equals something
 
if args[test] == equals then return ifexists else return ifnot end
 
if args[test] == equals then return ifexists else return ifnot end
else
+
else -- Existence of argument
 
if args[test] and args[test] ~= '' then return ifexists else return ifnot end
 
if args[test] and args[test] ~= '' then return ifexists else return ifnot end
 
end
 
end
 
end
 
end
local formatTable = {}
+
local formatTable = {} -- Table with definitions
 
-- Recursively dig into tables that could be parser hooks or argument tables.
 
-- Recursively dig into tables that could be parser hooks or argument tables.
 
local function formatStrInTable(formatStr)
 
local function formatStrInTable(formatStr)
if type(formatStr) ~= "table" then return formatStr end
+
if type(formatStr) ~= "table" then return formatStr end -- formatStr is a scalar
 
formatTable = formatStr
 
formatTable = formatStr
local hook = formatStr.hook
+
local hook = formatStr.hook -- Possible hook
local both = formatStr[2]
+
local both = formatStr[2] -- Second shield
 
if both then
 
if both then
 
local first = formatStrInTable(formatStr[1])
 
local first = formatStrInTable(formatStr[1])
 
local second = formatStrInTable(both)
 
local second = formatStrInTable(both)
return {first, second}
+
return {first, second} -- First and second shield
 
elseif hook then
 
elseif hook then
 
local hooksModule = require "Module:Road data/parser/hooks"
 
local hooksModule = require "Module:Road data/parser/hooks"
 
local hookFunction = hooksModule[hook] or error("Hook '" .. hook .. "' does not exist", 0)
 
local hookFunction = hooksModule[hook] or error("Hook '" .. hook .. "' does not exist", 0)
return formatStrInTable(hookFunction(formatStr, args))
+
return formatStrInTable(hookFunction(formatStr, args)) -- Call hook
else
+
else -- Switch on an argument
 
local arg = args[formatStr.arg or "route"]
 
local arg = args[formatStr.arg or "route"]
 
return formatStrInTable(formatStr[arg] or formatStr.default)
 
return formatStrInTable(formatStr[arg] or formatStr.default)
Line 47: Line 53:
 
end
 
end
 
local function parse(formatStr)
 
local function parse(formatStr)
local preprocessed = gsub(formatStr, prepattern, testArgs)
+
local preprocessed = gsub(formatStr, prepattern, testArgs) -- If statements
local parsedStr = gsub(preprocessed, pattern, args)
+
local parsedStr = gsub(preprocessed, pattern, args) -- Variable interpolation
local final = trim(parsedStr)
+
local final = trim(parsedStr) -- Trim extra spaces
if formatTable.ifexists then
+
if formatTable.ifexists then -- Existence test
 
local exists = ifexists(final)
 
local exists = ifexists(final)
 
if exists then
 
if exists then
Line 60: Line 66:
 
return final
 
return final
 
end
 
end
formatStr = formatStrInTable(formatStr)
+
formatStr = formatStrInTable(formatStr) -- Get formatStr
if not formatStr or formatStr == '' then return '' end
+
if not formatStr or formatStr == '' then return '' end -- Return empty string for empty formatStr
if type(formatStr) == 'table' then
+
if type(formatStr) == 'table' then -- Dual shields
 
local first = parse(formatStr[1])
 
local first = parse(formatStr[1])
 
local second = parse(formatStr[2])
 
local second = parse(formatStr[2])
Line 72: Line 78:
  
 
local function formatString(args, form)
 
local function formatString(args, form)
 +
-- Get format string/table from module based on jurisdiction and type
 
local function getTypeData(module, type)
 
local function getTypeData(module, type)
 +
-- Get data from module for particular type
 
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 '' end -- Empty string if module cannot be loaded
local typeTable = moduleData[type] or moduleData['']
+
local typeTable = moduleData[type] or moduleData[''] -- Type table or empty string default table
local defaultTable = moduleData[''] or {}
+
local defaultTable = moduleData[''] or {} -- Default table
 
if typeTable then
 
if typeTable then
 
local alias = typeTable.alias
 
local alias = typeTable.alias
if alias then
+
if alias then -- Type is an alias to another module
 
local aliasedModule = "Module:Road data/strings/" .. alias.module
 
local aliasedModule = "Module:Road data/strings/" .. alias.module
 
local aliasedType = alias.type
 
local aliasedType = alias.type
Line 90: Line 98:
 
end
 
end
 
 
local stateCountries = {USA = true, CAN = true}
+
local stateCountries = {USA = true, CAN = true} -- These countries have state/province modules
 
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
 
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'

Revision as of 18:59, 12 May 2016

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