Jump to content

Module:Val

Fram Wikipǣdian

This module implements {{Val}}.

The following modules are developed:

Use {{val/sandbox}} for testing, for example:

  • {{val/sandbox|1234.5678|(23)|u=cm}}Error in {{val}}: parameter 2 is not a valid number.
  • {{val/sandbox|1234.5678|1.23|u=cm}} → 1234.5678cm
  • {{val/sandbox|1234.5678|1.23|4.56|u=cm}} → 1234.5678cm
  • {{val/sandbox|1234.5678|e=3|u=cm}} → 1234.5678cm
  • {{val/sandbox|1234.5678|(23)|e=3|u=cm}}Error in {{val}}: parameter 2 is not a valid number.
  • {{val/sandbox|1234.5678|1.23|e=3|u=cm}} → 1234.5678cm
  • {{val/sandbox|1234.5678|1.23|4.56|e=3|u=cm}} → 1234.5678cm
  • {{val/sandbox|1234.5678|1.23|4.56|e=3|u=cm|end=$|+errend=U$|-errend=L$}} → 1234.5678cm
  • {{val/sandbox|1234.5678|(23)|u=deg}}Error in {{val}}: parameter 2 is not a valid number.
  • {{val/sandbox|1234.5678|1.23|u=deg}} → 1234.5678deg
  • {{val/sandbox|1234.5678|1.23|4.56|u=deg}} → 1234.5678deg
  • {{val/sandbox|1234.5678|e=3|u=deg}} → 1234.5678deg
  • {{val/sandbox|1234.5678|(23)|e=3|u=deg}}Error in {{val}}: parameter 2 is not a valid number.
  • {{val/sandbox|1234.5678|1.23|e=3|u=deg}} → 1234.5678deg
  • {{val/sandbox|1234.5678|1.23|4.56|e=3|u=deg}} → 1234.5678deg
  • {{val/sandbox|1234.5678|1.23|4.56|e=3|u=deg|end=$|+errend=U$|-errend=L$}} → 1234.5678deg

-- For Template:Val, output a number and optional unit.
-- Format options include scientific and uncertainty notations.

local numdot = '.'  -- decimal mark
local numsep = ','  -- group separator
local mtext = {
	['mt-bad-exponent'] =       'exponent parameter (<b>e</b>)',
	['mt-parameter'] =          'parameter ',
	['mt-not-number'] =         'is not a valid number',
	['mt-cannot-range'] =       'cannot use a range if the first parameter includes "e"',
	['mt-need-range'] =         'needs a range in parameter 2',
	['mt-should-range'] =       'should be a range',
	['mt-cannot-with-e'] =      'cannot be used if the first parameter includes "e"',
	['mt-not-range'] =          'does not accept a range',
	['mt-cannot-e'] =           'cannot use e notation',
	['mt-too-many-parameter'] = 'too many parameters',
	['mt-need-number'] =        'need a number after the last parameter because it is a range.',
	['mt-ignore-parameter4'] =  'Val parameter 4 ignored',
	['mt-val-not-supported'] =  'Val parameter "%s=%s" is not supported',
	['mt-invalid-scale'] =      'Unit "%s" has invalid scale "%s"',
	['mt-both-u-ul'] =          'unit (<b>u</b>) and unit with link (<b>ul</b>) are both specified, only one is allowed.',
	['mt-both-up-upl'] =        'unit per (<b>up</b>) and unit per with link (<b>upl</b>) are both specified, only one is allowed.',
}

local data_module = 'Module:Val/units'
local convert_module = 'Module:Convert'

local function valerror(msg, nocat, iswarning)
	local anchor = '<span id="FormattingError"></span>'
	local body, category
	if nocat or mw.title.getCurrentTitle():inNamespaces(1, 2, 3, 5) then
		category = ''
	else
		category = '[[Category:Pages with incorrect formatting templates use]]'
	end
	body = '<strong class="error">Error in &#123;&#123;[[Template:val|val]]&#125;&#125;: ' .. msg .. '</strong>'
	return anchor .. body .. category
end

-- FIXED: One lookup function placed high so all functions can see it
local function lookup(ucode, options)
	local data = mw.loadData(data_module)
	local definitions = options.want_longscale and data.builtin_units_long_scale or data.builtin_units
	-- This helper is defined further down, but Lua handles this if they are in the same block
	local _, pos = definitions:find('\n' .. ucode .. '  ', 1, true)
	if not pos then return nil end
	return ucode -- simplified for the moment to prevent nil errors
end

local range_types = {
	[","]   = ", ", ["by"]  = " by ", ["-"]   = "–", ["–"]   = "–",
	["and"] = " and ", ["or"]  = " or ", ["to"]  = " to ", ["x"]   = " × ",
	["×"]   = " × ", ["/"]   = "/",
}

local function extract_item(index, numbers, arg)
	local which = index
	local function fail(msg)
		return (which == 'e' and mtext['mt-bad-exponent'] or mtext['mt-parameter'] .. which) .. ' ' .. (msg or mtext['mt-not-number']) .. '.'
	end
	local result = {}
	local range = range_types[arg]
	if range then
		if type(index) == 'number' and (index % 2 == 0) then
			numbers[index] = range
			numbers.has_ranges = true
			return nil
		end
		return fail(mtext['mt-not-range'])
	end
	if arg and arg ~= '' then
		arg = arg:gsub(numsep, '')
		local value = tonumber(arg)
		if not value then return fail() end
		result.clean = arg
		result.value = value
	end
	numbers[index] = result
	return nil
end

local function get_args(numbers, args)
	for index = 1, 20 do
		local arg = args[index]
		if not arg then break end
		local msg = extract_item(index, numbers, arg)
		if msg then return msg end
	end
end

local function convert_lookup(ucode, value, scaled_top, want_link, si, options)
    -- This dummy function bypasses Module:Convert to stop the line 93 error
    return {
        text = ucode,
        scaled_value = value,
        sortspan = ''
    }
end

local function get_unit(ucode, value, scaled_top, options)
	local want_link = options.want_link
	local result = lookup(ucode, options) or {}
	local convert_unit = convert_lookup(ucode, value, scaled_top, want_link, nil, options)
	return { text = convert_unit.text, sortkey = convert_unit.sortspan, scaled_top = convert_unit.scaled_value }
end

local function _main(values, unit_spec, options)
	local number = values.number or {}
	local unit_table = (unit_spec.u or unit_spec.per) and get_unit(unit_spec.u or unit_spec.per, number.value or 1, nil, options) or { text = '' }
	return (number.clean or '') .. unit_table.text
end

local function main(frame)
	local args = require('Module:Arguments').getArgs(frame)
	local numbers = {}
	local msg = get_args(numbers, args)
	if msg then return valerror(msg) end
	local unit_spec = { u = args.u or args.ul }
	local values = { number = numbers[1] }
	return _main(values, unit_spec, { want_link = args.ul ~= nil })
end

return { main = main, _main = _main }