Module:Airs Table

From Fallen London Wiki

Documentation for this module may be created at Module:Airs Table/doc

local p = {}

local function pairs_cmp(pair1, pair2)
  if pair1[1] < pair2[1] then
    return true
  elseif pair1[1] == pair2[1] and pair1[2] < pair2[2] then
    return true
  else
    return false
  end
end

--[[
Iterate through the arguments passed to the parent template to extract known levels.

Input parameters:
    @args: the table of args to search
    @arg_prefix: the base name of the args to search
    @max_value: ranges higher than this number will be diregarded, the first range beyond it will be converted down to match this

Returns a sorted list of integer pairs, representing the start and end of a known range.
If a range contains only one number, it will be used for both numbers in the pair.
]]
local function level_ranges(args, arg_prefix, arg_max_value)
  local pattern = arg_prefix .. '%s*(%d+)%D*(%d*)'
  local max_value = tonumber(arg_max_value)
  local ranges = {}
  for k in pairs(args) do
    start_level, end_level = string.match(k, pattern)
    if end_level == '' then
      end_level = start_level
    end
    if start_level then
      if tonumber(start_level) <= max_value and tonumber(end_level) <= max_value then
      	table.insert(ranges, {tonumber(start_level), tonumber(end_level)})
	  end
	  if tonumber(start_level) <= max_value and tonumber(end_level) > max_value then
      	table.insert(ranges, {tonumber(start_level), max_value})
      end
    end
  end
  table.sort(ranges, pairs_cmp)
  return ranges
end

--[[
Generates the Wiki Note listing unaccounted numbers.

Input parameters:
    @ranges: a sorted list of integer pairs, the ranges of accounted numbers
    @max_value: an integer denoting the maximum value (100 for most Airs)
    @min_value: an integer denoting the maximum value (1 for most Airs)

If any numbers are unaccounted for, returns a string of a formatted Wiki Note
    containing an ordered list of unaccounted numbers.
Otherwise returns an empty string.
]]
local function find_unaccounted(ranges, min_value, max_value)
  local all_present = true
  local output = '<i><b>Wiki note:</b> Numbers unaccounted for: '
  local next_level = min_value
  table.insert(ranges, {max_value+1, max_value+1})
  for i,range in ipairs(ranges) do
    if next_level < range[1] then
      if not all_present then
        output = output .. ', '
      end
      all_present = false
      if next_level == range[1] - 1 then
        output = output .. next_level
      else
        output = output .. next_level .. ' – ' .. range[1] - 1
      end
    end
    next_level = range[2] + 1
  end
  if all_present then
    return ''
  else
    return output .. '</i>[[Category:Pages with missing variant text]]'
  end
end

--[[
Generate a Wiki Note listing numbers not yet accounted for in the table.

May have strange and unexpected behaviour if a row contains non-contiguous
values or if multiple rows have intersecting ranges.
]]
function p.unaccounted_airs_values(frame)
	local args = frame.args
	local parent_args = frame:getParent().args
	local arg_prefix = args.prefix or 'Level'
	local arg_min_value = tonumber(args.min_value) or 0
	local arg_max_value = tonumber(args.max_value) or 100

    local ranges = level_ranges(parent_args, arg_prefix, arg_max_value)
    return find_unaccounted(ranges, arg_min_value, arg_max_value)
end

return p