require('strict')local p = local getArgs = require('Module:Arguments').getArgs
-- Units accepted by that come in groups (e.g., "5 ft 6 in")local multiple =
-- Convert unit list to hashlocal mult_table = for _, v in ipairs(multiple) do mult_table[v] = trueend
-- Function to pull out values and units from numeric args-- Returns:-- values: list of numeric values, or "false" if no numeric argument is given-- units: list of units (str)-- value: if there is a last numeric value unpaired with a unit, it becomes the precisionlocal function parseValuesUnits(args) local values = local units = local indx = 1 local value = nil -- loop through numeric arguments in pairs while args[indx] or args[indx+1] do value = tonumber(args[indx]) -- if there is a unit, save in output lists if args[indx+1] then table.insert(values, value or false) table.insert(units, args[indx+1]) value = nil end indx = indx+2 end return values, units, valueend
-- Function to identify multiple units and rewrite them as new input or output groups-- Args:-- values, units: numeric values and units, as lists with same length-- Returns:-- newValues, newUnits: same lists rewrittenlocal function parseMultiples(values, units) local newValues = local newUnits = local i = 1 -- we will search for multiples with up to 4 entries (depending on length) local maxMultiple = math.min(4,#units-1) local valueFound = false -- flag to suppress second (and later) input values --- Hack for handling "stone": check if only value supplied is "lb" local onlyPounds = true for i = 1, #units do if values[i] and units[i] ~= 'lb' then onlyPounds = false break end end -- sweep through units while i <= #units do -- determine index of last possible unit that could contain a multiple local last_unit = math.min(i+maxMultiple-1,#units) local multipleFound = false -- try from longest multiple down to double multiple (prefer longest ones) for j = last_unit, i+1, -1 do local key = table.concat() if mult_table[key] then -- we found a multiple unit multipleFound = true -- Hack for "stone": add either 'lb' or multiple unit string to output units -- depending on whether 'lb' was the only unit string with a value if mw.ustring.sub(key,1,2)
-- Implement function p._convert(args) -- find all values and units in numeric args (and the precision, if it exists) local values, units, precision = parseValuesUnits(args) -- rewrite values and units if multiple units are found values, units = parseMultiples(values, units) -- sort input and outputs into different buckets local input_values = local input_units = local output_units = for i = 1, #units do if values[i] then table.insert(input_values, values[i]) table.insert(input_units, units[i]) else table.insert(output_units, units[i]) end end -- bail if nothing to convert if #input_values
0 then return nil end -- assemble argument list to local innerArgs = -- First, pass all input unit(s) for i, v in ipairs(input_values) do table.insert(innerArgs,v) table.insert(innerArgs,input_units[i]) end -- Then the output unit(s) [concatenated as single argument] table.insert(innerArgs,table.concat(output_units,"+")) if precision then table.insert(innerArgs,precision) -- last non-nil value contains precision end -- now handle all non-numeric arguments, passing to innerArgs.abbr = 'on' -- abbr=on by default for k, v in pairs(args) do if not tonumber(k) then innerArgs[k] = v end end -- Call with innerArgs local frame = mw.getCurrentFrame return frame:expandTemplateend
function p.convert(frame) local args = getArgs(frame) return p._convert(args) or ""end
return p