सामग्री पर जाएँ

"मॉड्यूल:Val": अवतरणों में अंतर

मुक्त ज्ञानकोश विकिपीडिया से
Content deleted Content added
fix gapnum crash with '1.'; load sandbox data if sandbox; convert should not apply its own scale if a built-in unit has a scale; fmt=gaps puts a gap in a 4-digit integer; avoid overflow with large e
handle action=list to list all built-in units so can preview changes; some unit validity checks; fix SI prefix if 'mc' or 'mu' (they are not recognized by convert); use sandbox units if sandbox used
पंक्ति 8: पंक्ति 8:
-- Return formatted message for {{val}} errors.
-- Return formatted message for {{val}} errors.
if is_test_run then -- LATER remove
if is_test_run then -- LATER remove
return 'Error: "' .. msg .. '"'
return 'Error: ' .. msg
end
end
local ret = mw.html.create('strong')
local ret = mw.html.create('strong')
पंक्ति 123: पंक्ति 123:
result.link = item
result.link = item
elseif n == 3 then
elseif n == 3 then
result.scale_text = item
result.scale = get_scale(item, ucode)
result.scale = get_scale(item, ucode)
else
else
result.more_ignored = item
break
break
end
end
end
end
if result.si then
local s = result.symbol
if ucode == 'mc' .. s or ucode == 'mu' .. s then
result.ucode = 'µ' .. s -- unit code for convert should be this
end
end
end
end
पंक्ति 174: पंक्ति 182:
end
end
if result.si then
if result.si then
ucode = result.ucode or ucode
si = { result.symbol, result.link }
si = { result.symbol, result.link }
use_result = false
use_result = false
पंक्ति 226: पंक्ति 235:
end
end
return { text = text, isangle = unit.isangle, sortkey = sortkey }
return { text = text, isangle = unit.isangle, sortkey = sortkey }
end

local function list_units()
-- Return wikitext to list the built-in units.
-- A unit code should not contain wikimarkup so don't bother escaping.
local data = mw.loadData(data_module)
local definitions = data.builtin_units .. data.builtin_units_long_scale
local last_was_blank = true
local n = 0
local result = {}
local function add(line)
if line == '' then
last_was_blank = true
else
if last_was_blank and n > 0 then
n = n + 1
result[n] = ''
end
last_was_blank = false
n = n + 1
result[n] = line
end
end
local si_prefixes = {
-- These are the prefixes recognized by convert; u is accepted for micro.
y = true,
z = true,
a = true,
f = true,
p = true,
n = true,
u = true,
['µ'] = true,
m = true,
c = true,
d = true,
da = true,
h = true,
k = true,
M = true,
G = true,
T = true,
P = true,
E = true,
Z = true,
Y = true,
}
local function is_valid(ucode, unit)
-- LATER Check for valid combinations of links/flags.
if unit and not unit.more_ignored then
if unit.si then
ucode = unit.ucode or ucode
local base = unit.symbol
local plen = #ucode - #base
if ucode == base or (plen > 0 and si_prefixes[ucode:sub(1, plen)] and ucode:sub(plen + 1) == base) then
return true
end
else
return true
end
end
return false
end
for line in definitions:gmatch('([^\n]*)\n') do
local pos, _ = line:find(' ', 1, true)
if pos then
local ucode = line:sub(1, pos - 1)
local unit = get_builtin_unit(ucode, '\n' .. line .. '\n')
if is_valid(ucode, unit) then
local flags, text
if unit.alias then
text = unit.symbol
else
text = '[[' .. unit.link .. '|' .. unit.symbol .. ']]'
end
if unit.isangle then
unit.nospace = nil -- don't show redundant flag
end
for _, f in ipairs({
{ 'alias', 'ALIAS' },
{ 'isangle', 'ANGLE' },
{ 'nospace', 'NOSPACE' },
{ 'si', 'SI' },
{ 'scale_text', unit.scale_text },
}) do
if unit[f[1]] then
local t = f[2]
if t:match('^%u+$') then
t = '<small>' .. t .. '</small>'
end
if flags then
flags = flags .. ' ' .. t
else
flags = t
end
end
end
if flags then
text = text .. ' • ' .. flags
end
add(ucode .. ' = ' .. text .. '<br />')
else
add(line .. ' ◆ <b>invalid definition</b><br />')
end
else
add(line)
end
end
return table.concat(result, '\n')
end
end


पंक्ति 279: पंक्ति 397:


local function _main(number, uncertainty, unit_spec, misc_tbl)
local function _main(number, uncertainty, unit_spec, misc_tbl)
data_module = 'Module:Val/units' .. (misc_tbl.sandbox and '/sandbox' or '')
local action = misc_tbl.action
if action then
if action == 'list' then
return list_units()
end
return valerror('invalid action "' .. action .. '".', misc_tbl.nocat)
end
local e_10 = misc_tbl.e
local e_10 = misc_tbl.e
local fmt = misc_tbl.fmt
local fmt = misc_tbl.fmt
पंक्ति 381: पंक्ति 507:


local function main(frame)
local function main(frame)
data_module = 'Module:Val/units'
if string.find(frame:getTitle(), 'sandbox', 1, true) then
data_module = data_module .. '/sandbox'
end
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs
local args = getArgs(frame, {wrappers = { 'Template:Val', 'Template:Val/sandboxlua' }})
local args = getArgs(frame, {wrappers = { 'Template:Val', 'Template:Val/sandboxlua' }})
पंक्ति 421: पंक्ति 543:
}
}
local misc_tbl = {
local misc_tbl = {
action = args.action,
align = args.a,
e = numbers.e,
e = numbers.e,
fmt = args.fmt,
nocat = nocat,
prefix = args.p,
prefix = args.p,
sandbox = string.find(frame:getTitle(), 'sandbox', 1, true) ~= nil,
suffix = args.s,
fmt = args.fmt or '',
align = args.a,
nocat = args.nocategory,
sortable = args.sortable,
sortable = args.sortable,
suffix = args.s,
}
}
number.nend = args['end']
number.nend = args['end']

07:27, 27 जुलाई 2015 का अवतरण

"इस मॉड्यूल हेतु प्रलेख मॉड्यूल:Val/doc पर बनाया जा सकता है"

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

local data_module  -- name of module defining units
local delimit_groups = require('Module:Gapnum').groups

local function valerror(msg, nocat)
	-- Return formatted message for {{val}} errors.
	if is_test_run then  -- LATER remove
		return 'Error: ' .. msg
	end
	local ret = mw.html.create('strong')
							:addClass('error')
							:wikitext('Error in &#123;&#123;Val&#125;&#125;: ' .. msg)
	-- Not in talk, user, user_talk, or wikipedia_talk
	if not nocat and not mw.title.getCurrentTitle():inNamespaces(1,2,3,5) then
		ret:wikitext('[[Category:Pages with incorrect formatting templates use]]')
	end
	return tostring(ret)
end

local function extract_number(index, numbers, args)
	-- Extract number from args[index] and store result in numbers[index]
	-- and return true if no argument or if argument is valid.
	-- The result is a table which is empty if there was no specified number.
	-- Input like 1e3 is regarded as invalid; should use e=3 parameter.
	-- Input commas are removed so 1,234 is the same as 1234.
	local result = {}
	local arg = args[index]  -- has been trimmed
	if arg and arg ~= '' then
		arg = arg:gsub(',', '')
		if arg:sub(1, 1) == '(' and arg:sub(-1) == ')' then
			result.parens = true
			arg = arg:sub(2, -2)
		end
		local minus = '−'
		local isnegative, propersign, prefix
		prefix, arg = arg:match('^(.-)([%d.]+)$')
		local value = tonumber(arg)
		if not value then
			return false
		end
		if arg:sub(1, 1) == '.' then
			arg = '0' .. arg
		end
		if prefix == '' then
			-- Ignore.
		elseif prefix == '±' then
			-- Display for first number, ignore for others.
			if index == 1 then
				propersign = '±'
			end
		elseif prefix == '+' then
			propersign = '+'
		elseif prefix == '-' or prefix == minus then
			propersign = minus
			isnegative = true
		else
			return false
		end
		result.clean = arg
		result.sign = propersign or ''
		result.value = isnegative and -value or value
	end
	numbers[index] = result
	return true
end

local function get_scale(text, ucode)
	-- Return the value of text as a number, or throw an error.
	-- This supports extremely basic expressions of the form:
	--   a / b
	--   a ^ b
	-- where a and b are numbers or 'pi'.
	local n = tonumber(text)
	if n then
		return n
	end
	n = text:gsub('pi', math.pi)
	for _, op in ipairs({ '/', '^' }) do
		local a, b = n:match('^(.-)' .. op .. '(.*)$')
		if a then
			a = tonumber(a)
			b = tonumber(b)
			if a and b then
				if op == '/' then
					return a / b
				elseif op == '^' then
					return a ^ b
				end
			end
			break
		end
	end
	error('Unit "' .. ucode .. '" has invalid scale "' .. text .. '"')
end

local function get_builtin_unit(ucode, definitions)
	-- Return table of information for the specified built-in unit, or nil if not known.
	-- Each defined unit code must be followed by two spaces (not tab characters).
	local _, pos = definitions:find('\n' .. ucode .. '  ', 1, true)
	if pos then
		local endline = definitions:find('\n', pos, true)
		if endline then
			local result = {}
			local n = 0
			local text = definitions:sub(pos, endline - 1):gsub('%s%s+', '\t')
			for item in (text .. '\t'):gmatch('(%S.-)\t') do
				if item == 'ALIAS' then
					result.alias = true
				elseif item == 'ANGLE' then
					result.isangle = true
					result.nospace = true
				elseif item == 'NOSPACE' then
					result.nospace = true
				elseif item == 'SI' then
					result.si = true
				else
					n = n + 1
					if n == 1 then
						result.symbol = item
					elseif n == 2 then
						result.link = item
					elseif n == 3 then
						result.scale_text = item
						result.scale = get_scale(item, ucode)
					else
						result.more_ignored = item
						break
					end
				end
			end
			if result.si then
				local s = result.symbol
				if ucode == 'mc' .. s or ucode == 'mu' .. s then
					result.ucode = 'µ' .. s  -- unit code for convert should be this
				end
			end
			if n >= 2 or (n >= 1 and result.alias) then
				return result
			end
			-- Ignore invalid definition, treating it as a comment.
		end
	end
end

local function convert_lookup(ucode, value, scaled_top, want_link, si, options)
	local lookup = require('Module:Convert/sandbox')._unit  -- TODO remove "/sandbox" when convert updated
	return lookup(ucode, {
			value = value,
			scaled_top = scaled_top,
			link = want_link,
			si = si,
			sort = options.want_sort,
		})
end

local function get_unit(ucode, value, scaled_top, options)
	local want_link = options.want_link
	if scaled_top then
		want_link = options.want_per_link
	end
	local data = mw.loadData(data_module)
	local result = options.want_longscale and
		get_builtin_unit(ucode, data.builtin_units_long_scale) or
		get_builtin_unit(ucode, data.builtin_units)
	local si, use_result
	if result then
		use_result = true
		if result.alias then
			ucode = result.symbol
			use_result = false
		end
		if result.scale then
			-- Setting si means convert will use the unit as given, and the sort key
			-- will be calculated from the value without any extra scaling that may
			-- occur if convert found the unit code. For example, if val defines the
			-- unit 'year' with a scale and if si were not set, convert would also apply
			-- its own scale because convert knows that a year is 31,557,600 seconds.
			si = { result.symbol, result.link }
			value = value * result.scale
		end
		if result.si then
			ucode = result.ucode or ucode
			si = { result.symbol, result.link }
			use_result = false
		end
	end
	local convert_unit = convert_lookup(ucode, value, scaled_top, want_link, si, options)
	if use_result then
		if want_link then
			result.text = '[[' .. result.link .. '|' .. result.symbol .. ']]'
		else
			result.text = result.symbol
		end
		result.sortkey = convert_unit.sortspan
		result.scaled_top = value
	else
		result = {
			text = convert_unit.text,
			sortkey = convert_unit.sortspan,
			scaled_top = convert_unit.scaled_value,
		}
	end
	return result
end

local function makeunit(value, options)
	-- Return table of information for the requested unit and options, or
	-- return nil if no unit.
	options = options or {}
	local unit
	local ucode = options.u
	local percode = options.per
	if ucode then
		unit = get_unit(ucode, value, nil, options)
	elseif percode then
		unit = { nospace = true, scaled_top = value }
	else
		return nil
	end
	local text = unit.text or ''
	local sortkey = unit.sortkey
	if percode then
		local function bracketed(code, text)
			return code:find('[*./]') and '(' .. text .. ')' or text
		end
		local perunit = get_unit(percode, 1, unit.scaled_top, options)
		text = (ucode and bracketed(ucode, text) or '') ..
				'/' .. bracketed(percode, perunit.text)
		sortkey = perunit.sortkey
	end
	if not unit.nospace then
		text = '&nbsp;' .. text
	end
	return { text = text, isangle = unit.isangle, sortkey = sortkey }
end

local function list_units()
	-- Return wikitext to list the built-in units.
	-- A unit code should not contain wikimarkup so don't bother escaping.
	local data = mw.loadData(data_module)
	local definitions = data.builtin_units .. data.builtin_units_long_scale
	local last_was_blank = true
	local n = 0
	local result = {}
	local function add(line)
		if line == '' then
			last_was_blank = true
		else
			if last_was_blank and n > 0 then
				n = n + 1
				result[n] = ''
			end
			last_was_blank = false
			n = n + 1
			result[n] = line
		end
	end
	local si_prefixes = {
		-- These are the prefixes recognized by convert; u is accepted for micro.
		y = true,
		z = true,
		a = true,
		f = true,
		p = true,
		n = true,
		u = true,
		['µ'] = true,
		m = true,
		c = true,
		d = true,
		da = true,
		h = true,
		k = true,
		M = true,
		G = true,
		T = true,
		P = true,
		E = true,
		Z = true,
		Y = true,
	}
	local function is_valid(ucode, unit)
		-- LATER Check for valid combinations of links/flags.
		if unit and not unit.more_ignored then
			if unit.si then
				ucode = unit.ucode or ucode
				local base = unit.symbol
				local plen = #ucode - #base
				if ucode == base or (plen > 0 and si_prefixes[ucode:sub(1, plen)] and ucode:sub(plen + 1) == base) then
					return true
				end
			else
				return true
			end
		end
		return false
	end
	for line in definitions:gmatch('([^\n]*)\n') do
		local pos, _ = line:find('  ', 1, true)
		if pos then
			local ucode = line:sub(1, pos - 1)
			local unit = get_builtin_unit(ucode, '\n' .. line .. '\n')
			if is_valid(ucode, unit) then
				local flags, text
				if unit.alias then
					text = unit.symbol
				else
					text = '[[' .. unit.link .. '|' .. unit.symbol .. ']]'
				end
				if unit.isangle then
					unit.nospace = nil  -- don't show redundant flag
				end
				for _, f in ipairs({
						{ 'alias', 'ALIAS' },
						{ 'isangle', 'ANGLE' },
						{ 'nospace', 'NOSPACE' },
						{ 'si', 'SI' },
						{ 'scale_text', unit.scale_text },
					}) do
					if unit[f[1]] then
						local t = f[2]
						if t:match('^%u+$') then
							t = '<small>' .. t .. '</small>'
						end
						if flags then
							flags = flags .. ' ' .. t
						else
							flags = t
						end
					end
				end
				if flags then
					text = text .. ' • ' .. flags
				end
				add(ucode .. ' = ' .. text .. '<br />')
			else
				add(line .. ' ◆ <b>invalid definition</b><br />')
			end
		else
			add(line)
		end
	end
	return table.concat(result, '\n')
end

local function delimit(sign, numstr, fmt)
	-- Return sign and numstr (unsigned digits or '.' only) after formatting.
	-- Four-digit integers are not formatted with gaps.
	fmt = (fmt or ''):lower()
	if fmt == 'none' or (fmt == '' and #numstr == 4 and numstr:match('^%d+$')) then
		return sign .. numstr
	end
	-- Group number by integer and decimal parts.
	-- If there is no decimal part, delimit_groups returns only one table.
	local ipart, dpart = delimit_groups(numstr)
	local result
	if fmt == 'commas' then
		result = sign .. table.concat(ipart, ',')
		if dpart then
			result = result .. '.' .. table.concat(dpart)
		end
	else
		-- Delimit with a small gap by default.
		local groups = {}
		groups[1] = table.remove(ipart, 1)
		for _, v in ipairs(ipart) do
			table.insert(groups, '<span style="margin-left:.25em">' .. v .. '</span>')
		end
		if dpart then
			table.insert(groups, '.' .. (table.remove(dpart, 1) or ''))
			for _, v in ipairs(dpart) do
				table.insert(groups, '<span style="margin-left:.25em">' .. v .. '</span>')
			end
		end
		result = table.concat(groups)
		-- LATER Is the following needed?
		--       It is for compatibility with {{val}} which uses {{val/delimitnum}}.
		result = '<span style="white-space:nowrap">' .. sign .. result .. '</span>'
	end
	return result
end

local function sup_sub(sup, sub, align)
	-- Return the same result as Module:Su except val defaults to align=right.
	if align == 'l' or align == 'left' then
		align = 'left'
	elseif align == 'c' or align == 'center' then
		align = 'center'
	else
		align = 'right'
	end
	return '<span style="display:inline-block;margin-bottom:-0.3em;vertical-align:-0.4em;line-height:1.2em;font-size:85%;text-align:' ..
		align .. '">' .. sup .. '<br />' .. sub .. '</span>'
end

local function _main(number, uncertainty, unit_spec, misc_tbl)
	data_module = 'Module:Val/units' .. (misc_tbl.sandbox and '/sandbox' or '')
	local action = misc_tbl.action
	if action then
		if action == 'list' then
			return list_units()
		end
		return valerror('invalid action "' .. action .. '".', misc_tbl.nocat)
	end
	local e_10 = misc_tbl.e
	local fmt = misc_tbl.fmt
	-- Unit.
	local sortkey
	local want_sort = not (misc_tbl.sortable == 'off')
	local sort_value = 1
	if want_sort then
		sort_value = number.value or 1
		if e_10.value and sort_value ~= 0 then
			-- The 'if' avoids {{val|0|e=1234}} giving an invalid sort_value due to overflow.
			sort_value = sort_value * 10^e_10.value
		end
	end
	local unit_table = makeunit(sort_value, {
						u = unit_spec.u,
						want_link = unit_spec.want_link,
						per = unit_spec.per,
						want_per_link = unit_spec.want_per_link,
						want_longscale = unit_spec.want_longscale,
						want_sort = want_sort,
					})
	if unit_table then
		if want_sort then
			sortkey = unit_table.sortkey
		end
	else
		unit_table = { text = '' }
		if want_sort then
			sortkey = convert_lookup('dummy', sort_value, nil, nil, nil, { want_sort = true }).sortspan
		end
	end
	-- Uncertainty.
	local unc_text
	local paren_left, paren_right = '', ''
	local uncU = uncertainty.upper.clean
	if uncU and number.clean then
		-- Cannot enter an uncertainty without a preceding number, however, if it were
		-- possible, the uncertainty should be ignored to avoid displaying junk.
		local uncL = uncertainty.lower.clean
		if uncL then
			uncU =  delimit('+', uncU, fmt) .. (uncertainty.upper.errend or '')
			uncL =  delimit('−', uncL, fmt) .. (uncertainty.lower.errend or '')
			if unit_table.isangle then
				uncU = uncU .. unit_table.text
				uncL = uncL .. unit_table.text
			end
			unc_text = '<span style="margin-left:0.3em;">' .. sup_sub(uncU, uncL, misc_tbl.align) .. '</span>'
		else
			if uncertainty.upper.parens then
				unc_text = '(' .. uncU .. ')'  -- template does not delimit
			else
				unc_text = '<span style="margin-left:0.3em;margin-right:0.15em">±</span>' .. delimit('', uncU, fmt)
				if e_10.clean then
					paren_left = '('
					paren_right = ')'
				end
			end
			if uncertainty.errend then
				unc_text = unc_text .. uncertainty.errend
			end
			if unit_table.isangle then
				unc_text = unc_text .. unit_table.text
			end
		end
	end
	local e_text, n_text
	if number.clean then
		n_text = delimit(number.sign, number.clean, fmt) .. (number.nend  or '')
		if not uncertainty.upper.parens and unit_table.isangle then
			n_text = n_text .. unit_table.text
		end
	else
		n_text = ''
		if not e_10.clean then
			e_10.clean = '0'
			e_10.sign = ''
		end
	end
	if e_10.clean then
		e_text = '10<sup>' .. delimit(e_10.sign, e_10.clean, fmt) .. '</sup>'
		if number.clean then
			e_text = '<span style="margin-left:0.25em;margin-right:0.15em">×</span>' .. e_text
		end
	else
		e_text = ''
	end
	return table.concat({
			'<span class="nowrap">',
			sortkey or '',
			misc_tbl.prefix or '',
			paren_left,
			n_text,
			unc_text or '',
			paren_right,
			e_text,
			unit_table.isangle and '' or unit_table.text,
			misc_tbl.suffix or '',
			'</span>'
		})
end

local function main(frame)
	local getArgs = require('Module:Arguments').getArgs
	local args = getArgs(frame, {wrappers = { 'Template:Val', 'Template:Val/sandboxlua' }})
	local nocat = args.nocategory
	local numbers = {}
	local checks = {
		-- index, description
		{ 1, 'first parameter' },
		{ 2, 'second parameter' },
		{ 3, 'third parameter' },
		{ 'e', 'exponent parameter (<b>e</b>)' },
	}
	for _, item in ipairs(checks) do
		if not extract_number(item[1], numbers, args) then
			return valerror(item[2] .. ' is not a valid number.', nocat)
		end
	end
	if args.u and args.ul then
		return valerror('unit (<b>u</b>) and unit with link (<b>ul</b>) are both specified, only one is allowed.', nocat)
	end
	if args.up and args.upl then
		return valerror('unit per (<b>up</b>) and unit per with link (<b>upl</b>) are both specified, only one is allowed.', nocat)
	end
	local number = numbers[1]
	local uncertainty = {
			upper = numbers[2],
			lower = numbers[3],
			errend = args.errend,
		}
	local unit_spec = {
			u = args.ul or args.u,
			want_link = args.ul ~= nil,
			per = args.upl or args.up,
			want_per_link = args.upl ~= nil,
			want_longscale = (args.longscale or args.long_scale or args['long scale']) == 'on',
		}
	local misc_tbl = {
			action = args.action,
			align = args.a,
			e = numbers.e,
			fmt = args.fmt,
			nocat = nocat,
			prefix = args.p,
			sandbox = string.find(frame:getTitle(), 'sandbox', 1, true) ~= nil,
			sortable = args.sortable,
			suffix = args.s,
		}
	number.nend = args['end']
	uncertainty.upper.errend = args['+errend']
	uncertainty.lower.errend = args['-errend']
	return _main(number, uncertainty, unit_spec, misc_tbl)
end

return { main = main, _main = _main }