Module:Convert: Difference between revisions
Content added Content deleted
m (1 revision imported: Templates) |
(update from sandbox per Template talk:Convert#Module version 29) |
||
Line 136: | Line 136: | ||
local spell_module -- name of module that can spell numbers |
local spell_module -- name of module that can spell numbers |
||
local speller -- function from that module to handle spelling (set if |
local speller -- function from that module to handle spelling (set if needed) |
||
local wikidata_module, wikidata_data_module -- names of Wikidata modules |
|||
local wikidata_code, wikidata_data -- exported tables from those modules (set if needed) |
|||
local function set_config(args) |
local function set_config(args) |
||
Line 147: | Line 149: | ||
text_module = "Module:Convert/text" .. sandbox |
text_module = "Module:Convert/text" .. sandbox |
||
extra_module = "Module:Convert/extra" .. sandbox |
extra_module = "Module:Convert/extra" .. sandbox |
||
wikidata_module = "Module:Convert/wikidata" .. sandbox |
|||
wikidata_data_module = "Module:Convert/wikidata/data" .. sandbox |
|||
spell_module = "Module:ConvertNumeric" |
spell_module = "Module:ConvertNumeric" |
||
data_code = mw.loadData(data_module) |
data_code = mw.loadData(data_module) |
||
Line 255: | Line 259: | ||
end |
end |
||
local function wanted_category( |
local function wanted_category(catkey, catsort, want_warning) |
||
-- Return |
-- Return message category if it is wanted in current namespace, |
||
-- otherwise return ''. |
|||
-- This is so tracking categories only include pages that need correction. |
|||
local cat |
|||
local title = mw.title.getCurrentTitle() |
local title = mw.title.getCurrentTitle() |
||
if title then |
if title then |
||
Line 264: | Line 269: | ||
for _, v in ipairs(split(config.nscat or nsdefault, ',')) do |
for _, v in ipairs(split(config.nscat or nsdefault, ',')) do |
||
if namespace == tonumber(v) then |
if namespace == tonumber(v) then |
||
cat = text_code.all_categories[want_warning and 'warning' or catkey] |
|||
return cat |
|||
if catsort and catsort ~= '' and cat:sub(-2) == ']]' then |
|||
cat = cat:sub(1, -3) .. '|' .. mw.text.nowiki(usub(catsort, 1, 20)) .. ']]' |
|||
end |
|||
break |
|||
end |
end |
||
end |
end |
||
end |
end |
||
return cat or '' |
|||
end |
end |
||
local function message(mcode) |
local function message(parms, mcode, is_warning) |
||
-- Return wikitext for an error message, including category if specified |
-- Return wikitext for an error message, including category if specified |
||
-- for the message type. |
-- for the message type. |
||
-- mcode = numbered table specifying the message: |
-- mcode = numbered table specifying the message: |
||
-- mcode[1] = 'cvt_xxx' (string used as a key to get message info) |
-- mcode[1] = 'cvt_xxx' (string used as a key to get message info) |
||
-- mcode[2] = 'parm1' (string to replace |
-- mcode[2] = 'parm1' (string to replace '$1' if any in message) |
||
-- mcode[3] = 'parm2' (string to replace |
-- mcode[3] = 'parm2' (string to replace '$2' if any in message) |
||
-- mcode[4] = 'parm3' (string to replace |
-- mcode[4] = 'parm3' (string to replace '$3' if any in message) |
||
local msg |
local msg |
||
if type(mcode) == 'table' then |
|||
local nowiki = mw.text.nowiki |
|||
if mcode[1] == 'cvt_no_output' then |
|||
-- Some errors should cause convert to output an empty string, |
|||
-- for example, for an optional field in an infobox. |
|||
return '' |
|||
end |
|||
msg = text_code.all_messages[mcode[1]] |
|||
end |
|||
parms.have_problem = true |
|||
local function subparm(fmt, ...) |
|||
local rep = {} |
|||
for i, v in ipairs({...}) do |
|||
rep['$' .. i] = v |
|||
end |
|||
return (fmt:gsub('$%d+', rep)) |
|||
end |
|||
if msg then |
if msg then |
||
local parts = {} |
local parts = {} |
||
Line 305: | Line 330: | ||
append = '...' |
append = '...' |
||
end |
end |
||
s = nowiki(s) .. (append or '') |
s = mw.text.nowiki(s) .. (append or '') |
||
else |
else |
||
s = '?' |
s = '?' |
||
end |
end |
||
parts[i] = s |
parts['$' .. i] = s |
||
end |
|||
local function ispreview() |
|||
-- Return true if a prominent message should be shown. |
|||
if parms.test == 'preview' or parms.test == 'nopreview' then |
|||
-- For testing, can preview a real message or simulate a preview |
|||
-- when running automated tests. |
|||
return parms.test == 'preview' |
|||
end |
|||
local success, revid = pcall(function () |
|||
return (parms.frame):preprocess('{{REVISIONID}}') end) |
|||
return success and (revid == '') |
|||
end |
end |
||
local want_warning = is_warning and |
|||
local title = format(msg[1] or 'Missing message', parts[1], parts[2], parts[3]) |
|||
not config.warnings and -- show unobtrusive warnings if config.warnings not configured |
|||
local text = msg[2] or 'Missing message' |
|||
not msg.nowarn -- but use msg settings, not standard warning, if specified |
|||
local cat = wanted_category(text_code.all_categories[msg[3]]) or '' |
|||
local title = string.gsub(msg[1] or 'Missing message', '$%d+', parts) |
|||
local text = want_warning and '*' or msg[2] or 'Missing message' |
|||
local cat = wanted_category(msg[3], mcode[2], want_warning) |
|||
local anchor = msg[4] or '' |
local anchor = msg[4] or '' |
||
local fmtkey = ispreview() and 'cvt_format_preview' or |
|||
local fmt = text_code.all_messages[msg.format or 'cvt_format'] or 'convert: bug' |
|||
(want_warning and 'cvt_format2' or msg.format or 'cvt_format') |
|||
title = title:gsub('"', '"') |
|||
local fmt = text_code.all_messages[fmtkey] or 'convert: bug' |
|||
return format(fmt, anchor, title, text, cat) |
|||
return subparm(fmt, title:gsub('"', '"'), text, cat, anchor) |
|||
end |
end |
||
return 'Convert internal error: unknown message' |
return 'Convert internal error: unknown message' |
||
Line 324: | Line 364: | ||
function add_warning(parms, level, key, text1, text2) -- for forward declaration above |
function add_warning(parms, level, key, text1, text2) -- for forward declaration above |
||
-- If enabled, add a warning that will be displayed after the convert result. |
-- If enabled, add a warning that will be displayed after the convert result. |
||
-- A higher level is more verbose: more kinds of warnings are displayed. |
|||
-- To reduce output noise, only the first warning is displayed. |
-- To reduce output noise, only the first warning is displayed. |
||
if config.warnings or |
if level <= (tonumber(config.warnings) or 1) then |
||
if |
if parms.warnings == nil then |
||
parms.warnings = message(parms, { key, text1, text2 }, true) |
|||
parms.warnings = message({ key, text1, text2 }) |
|||
end |
|||
end |
end |
||
end |
end |
||
Line 350: | Line 389: | ||
success, speller = pcall(get_speller, spell_module) |
success, speller = pcall(get_speller, spell_module) |
||
if not success or type(speller) ~= 'function' then |
if not success or type(speller) ~= 'function' then |
||
add_warning(parms, 1, 'cvt_no_spell') |
add_warning(parms, 1, 'cvt_no_spell', 'spell') |
||
return nil |
return nil |
||
end |
end |
||
Line 372: | Line 411: | ||
-- If no altitude given, use default (zero altitude = sea level). |
-- If no altitude given, use default (zero altitude = sea level). |
||
-- Table gives speed of sound in miles per hour at various altitudes: |
-- Table gives speed of sound in miles per hour at various altitudes: |
||
-- altitude = -17,499 to |
-- altitude = -17,499 to 402,499 feet |
||
-- mach_table[a + 4] = s where |
-- mach_table[a + 4] = s where |
||
-- a = (altitude / 5000) rounded to nearest integer (-3 to |
-- a = (altitude / 5000) rounded to nearest integer (-3 to 80) |
||
-- s = speed of sound (mph) at that altitude |
-- s = speed of sound (mph) at that altitude |
||
-- LATER: Should calculate result from an interpolation between the next |
-- LATER: Should calculate result from an interpolation between the next |
||
Line 384: | Line 423: | ||
660.1, 660.1, 660.1, 662.0, 664.3, 666.5, 668.9, 671.1, 673.4, 675.6, -- 11 to 20 |
660.1, 660.1, 660.1, 662.0, 664.3, 666.5, 668.9, 671.1, 673.4, 675.6, -- 11 to 20 |
||
677.9, 683.7, 689.9, 696.0, 702.1, 708.1, 714.0, 719.9, 725.8, 731.6, -- 21 to 30 |
677.9, 683.7, 689.9, 696.0, 702.1, 708.1, 714.0, 719.9, 725.8, 731.6, -- 21 to 30 |
||
737.3, 737.7, 737.7, 736.2, 730.5, 724.6, 718.8, 712.9, 707.0, 701. |
737.3, 737.7, 737.7, 736.2, 730.5, 724.6, 718.8, 712.9, 707.0, 701.0, -- 31 to 40 |
||
695.0, 688.9, 682.8, 676.6, 670.4, 664.1, 657.8, 652.9, 648.3, 643.7, -- 41 to 50 |
695.0, 688.9, 682.8, 676.6, 670.4, 664.1, 657.8, 652.9, 648.3, 643.7, -- 41 to 50 |
||
639.1, 634.4, 629.6, 624.8, 620.0, 615.2, 613.2, 613.2, 613.2, 613.5, -- 51 to 60 |
639.1, 634.4, 629.6, 624.8, 620.0, 615.2, 613.2, 613.2, 613.2, 613.5, -- 51 to 60 |
||
614.4, 615.3, 616.7, 619.8, 623.4, 629.7, 635.0, 641.1, 650.6, 660.0, -- 61 to 70 |
|||
672.5, 674.3, 676.1, 677.9, 679.7, 681.5, 683.3, 685.1, 686.8, 688.6, -- 71 to 80 |
|||
} |
} |
||
altitude = altitude or 0 |
altitude = altitude or 0 |
||
Line 396: | Line 437: | ||
if a < -3 then |
if a < -3 then |
||
a = -3 |
a = -3 |
||
elseif a > |
elseif a > 80 then |
||
a = |
a = 80 |
||
end |
end |
||
return mach_table[a + 4] * 0.44704 -- mph converted to m/s |
return mach_table[a + 4] * 0.44704 -- mph converted to m/s |
||
Line 403: | Line 444: | ||
-- END: Code required only for built-in units. |
-- END: Code required only for built-in units. |
||
------------------------------------------------------------------------ |
------------------------------------------------------------------------ |
||
local function add_style(parms, class) |
|||
-- Add selected template style to parms if not already present. |
|||
parms.templatestyles = parms.templatestyles or {} |
|||
if not parms.templatestyles[class] then |
|||
parms.templatestyles[class] = parms.frame:extensionTag({ |
|||
name = 'templatestyles', args = { src = text_code.titles[class] } |
|||
}) |
|||
end |
|||
end |
|||
local function get_styles(parms) |
|||
-- Return string of required template styles, empty if none. |
|||
if parms.templatestyles then |
|||
local t = {} |
|||
for _, v in pairs(parms.templatestyles) do |
|||
table.insert(t, v) |
|||
end |
|||
return table.concat(t) |
|||
end |
|||
return '' |
|||
end |
|||
local function get_range(word) |
local function get_range(word) |
||
Line 505: | Line 568: | ||
if key == 'symbol' then |
if key == 'symbol' then |
||
value = self.si_prefix .. self._symbol |
value = self.si_prefix .. self._symbol |
||
if value == 'l' then value = 'L' end |
|||
elseif key == 'sym_us' then |
elseif key == 'sym_us' then |
||
value = rawget(self, '_sym_us') |
value = rawget(self, '_sym_us') |
||
Line 551: | Line 615: | ||
-- This is never called to determine a unit name or link because per units |
-- This is never called to determine a unit name or link because per units |
||
-- are handled as a special case. |
-- are handled as a special case. |
||
-- Similarly, the default output is handled elsewhere |
-- Similarly, the default output is handled elsewhere, and for a symbol |
||
-- this is only called from get_default() for default_exceptions. |
|||
__index = function (self, key) |
__index = function (self, key) |
||
local value |
local value |
||
Line 576: | Line 641: | ||
} |
} |
||
local function make_per(unit_table, ulookup) |
local function make_per(unitcode, unit_table, ulookup) |
||
-- Return true, t where t is a per unit with unit codes expanded to unit tables, |
-- Return true, t where t is a per unit with unit codes expanded to unit tables, |
||
-- or return false, t where t is an error message table. |
-- or return false, t where t is an error message table. |
||
local result = { |
local result = { |
||
unitcode = unitcode, |
|||
utype = unit_table.utype, |
|||
per = {} |
|||
} |
|||
override_from(result, unit_table, { 'invert', 'iscomplex', 'default', 'link', 'symbol', 'symlink' }) |
override_from(result, unit_table, { 'invert', 'iscomplex', 'default', 'link', 'symbol', 'symlink' }) |
||
result.symbol_raw = (result.symbol or false) -- to distinguish between a defined exception and a metatable calculation |
result.symbol_raw = (result.symbol or false) -- to distinguish between a defined exception and a metatable calculation |
||
Line 632: | Line 701: | ||
-- Wikignomes may also put two spaces or " " in combinations, so |
-- Wikignomes may also put two spaces or " " in combinations, so |
||
-- replace underscore, " ", and multiple spaces with a single space. |
-- replace underscore, " ", and multiple spaces with a single space. |
||
utable = utable or all_units |
utable = utable or parms.unittable or all_units |
||
fails = fails or {} |
fails = fails or {} |
||
depth = depth and depth + 1 or 1 |
depth = depth and depth + 1 or 1 |
||
Line 645: | Line 714: | ||
end |
end |
||
unitcode = unitcode:gsub('_', ' '):gsub(' ', ' '):gsub(' +', ' ') |
unitcode = unitcode:gsub('_', ' '):gsub(' ', ' '):gsub(' +', ' ') |
||
local function call_make_per(t) |
|||
return make_per(unitcode, t, |
|||
function (ucode) return lookup(parms, ucode, 'no_combination', utable, fails, depth) end |
|||
) |
|||
end |
|||
local t = utable[unitcode] |
local t = utable[unitcode] |
||
if t then |
if t then |
||
Line 666: | Line 740: | ||
end |
end |
||
if t.per then |
if t.per then |
||
return call_make_per(t) |
|||
return make_per(t, function (ucode) return lookup(parms, ucode, 'no_combination', utable, fails, depth) end) |
|||
end |
end |
||
local combo = t.combination -- nil or a table of unitcodes |
local combo = t.combination -- nil or a table of unitcodes |
||
Line 686: | Line 760: | ||
end |
end |
||
local result = shallow_copy(t) |
local result = shallow_copy(t) |
||
result.unitcode = unitcode |
|||
if result.prefixes then |
if result.prefixes then |
||
result.si_name = '' |
result.si_name = '' |
||
Line 704: | Line 779: | ||
if t and t.prefixes then |
if t and t.prefixes then |
||
local result = shallow_copy(t) |
local result = shallow_copy(t) |
||
result.unitcode = unitcode |
|||
result.si_name = parms.opt_sp_us and si.name_us or si.name |
result.si_name = parms.opt_sp_us and si.name_us or si.name |
||
result.si_prefix = si.prefix or prefix |
result.si_prefix = si.prefix or prefix |
||
result.scale = t.scale * 10 ^ (si.exponent * t.prefixes) |
result.scale = t.scale * 10 ^ (si.exponent * t.prefixes) |
||
return true, setmetatable(result, unit_prefixed_mt) |
return true, setmetatable(result, unit_prefixed_mt) |
||
end |
|||
end |
|||
end |
|||
-- Accept any unit with an engineering notation prefix like "e6cuft" |
|||
-- (million cubic feet), but not chained prefixes like "e3e6cuft", |
|||
-- and not if the unit is a combination or multiple, |
|||
-- and not if the unit has an offset or is a built-in. |
|||
-- Only en digits are accepted. |
|||
local has_plus = unitcode:find('+', 1, true) |
|||
if not has_plus then |
|||
local exponent, baseunit = unitcode:match('^e(%d+)(.*)') |
|||
if exponent then |
|||
local engscale = text_code.eng_scales[exponent] |
|||
if engscale then |
|||
local success, result = lookup(parms, baseunit, 'no_combination', utable, fails, depth) |
|||
if success and not (result.offset or result.builtin or result.engscale) then |
|||
result.defkey = unitcode -- key to lookup default exception |
|||
result.engscale = engscale |
|||
result.scale = result.scale * 10 ^ tonumber(exponent) |
|||
return true, result |
|||
end |
|||
end |
end |
||
end |
end |
||
Line 738: | Line 793: | ||
local err_is_fatal |
local err_is_fatal |
||
local combo = collection() |
local combo = collection() |
||
if |
if unitcode:find('+', 1, true) then |
||
err_is_fatal = true |
err_is_fatal = true |
||
for item in (unitcode .. '+'):gmatch('%s*(.-)%s*%+') do |
for item in (unitcode .. '+'):gmatch('%s*(.-)%s*%+') do |
||
Line 775: | Line 830: | ||
if success or err_is_fatal then |
if success or err_is_fatal then |
||
return success, result |
return success, result |
||
end |
|||
end |
|||
-- Accept any unit with an engineering notation prefix like "e6cuft" |
|||
-- (million cubic feet), but not chained prefixes like "e3e6cuft", |
|||
-- and not if the unit is a combination or multiple, |
|||
-- and not if the unit has an offset or is a built-in. |
|||
-- Only en digits are accepted. |
|||
local exponent, baseunit = unitcode:match('^e(%d+)(.*)') |
|||
if exponent then |
|||
local engscale = text_code.eng_scales[exponent] |
|||
if engscale then |
|||
local success, result = lookup(parms, baseunit, 'no_combination', utable, fails, depth) |
|||
if success and not (result.offset or result.builtin or result.engscale) then |
|||
result.unitcode = unitcode -- 'e6cuft' not 'cuft' |
|||
result.defkey = unitcode -- key to lookup default exception |
|||
result.engscale = engscale |
|||
result.scale = result.scale * 10 ^ tonumber(exponent) |
|||
return true, result |
|||
end |
|||
end |
end |
||
end |
end |
||
Line 784: | Line 858: | ||
-- Engineering notation (apart from at start and which has been stripped before here), |
-- Engineering notation (apart from at start and which has been stripped before here), |
||
-- is not supported so do not make a per unit if find text like 'e3' in unitcode. |
-- is not supported so do not make a per unit if find text like 'e3' in unitcode. |
||
local success, result = |
local success, result = call_make_per({ per = {top, bottom} }) |
||
if success then |
if success then |
||
return true, result |
return true, result |
||
Line 911: | Line 985: | ||
end |
end |
||
return sep .. id .. mid |
return sep .. id .. mid |
||
end |
|||
local function change_sign(text) |
|||
-- Change sign of text for correct appearance because it is negated. |
|||
if text:sub(1, 1) == '-' then |
|||
return text:sub(2) |
|||
end |
|||
return '-' .. text |
|||
end |
end |
||
Line 1,002: | Line 1,068: | ||
-- When using gaps, they are inserted before and after the decimal mark. |
-- When using gaps, they are inserted before and after the decimal mark. |
||
-- Separators are inserted only before the decimal mark. |
-- Separators are inserted only before the decimal mark. |
||
-- A trailing dot (as in '123.') is removed because their use appears to |
|||
-- be accidental, and such a number should be shown as '123' or '123.0'. |
|||
-- It is useful for convert to suppress the dot so, for example, '4000.' |
|||
-- is a simple way of indicating that all the digits are significant. |
|||
if text:sub(-1) == '.' then |
|||
text = text:sub(1, -2) |
|||
end |
|||
if #text < 4 or parms.opt_nocomma or numsep == '' then |
if #text < 4 or parms.opt_nocomma or numsep == '' then |
||
return from_en(text) |
return from_en(text) |
||
Line 1,069: | Line 1,142: | ||
local fracfmt = { |
local fracfmt = { |
||
{ -- Like {{frac}} (fraction slash). |
{ -- Like {{frac}} (fraction slash). |
||
'<span class="frac" role="math">{SIGN}<span class="num">{NUM}</span>⁄<span class="den">{DEN}</span></span>', -- 1/2 |
|||
-- 1/2 : sign, numerator, denominator |
|||
'<span class="frac" role="math">{SIGN}{WHOLE}<span class="sr-only">+</span><span class="num">{NUM}</span>⁄<span class="den">{DEN}</span></span>', -- 1+2/3 |
|||
-- 1+2/3 : signed_wholenumber, numerator, denominator |
|||
style = 'frac', |
|||
'<span class="frac nowrap">%s<sup>%s</sup>⁄<sub>%s</sub></span>', |
|||
'<span class="frac nowrap">%s<span class="visualhide"> </span><sup>%s</sup>⁄<sub>%s</sub></span>', |
|||
}, |
}, |
||
{ -- Like {{sfrac}} (fraction horizontal bar). |
{ -- Like {{sfrac}} (stacked fraction, that is, horizontal bar). |
||
'<span class="sfrac tion" role="math">{SIGN}<span class="num">{NUM}</span><span class="sr-only">/</span><span class="den">{DEN}</span></span>', -- 1//2 |
|||
-- 1//2 : sign, numerator, denominator (sign should probably be before the fraction, but then it can wrap, and html is already too long) |
|||
'<span class="sfrac" role="math">{SIGN}{WHOLE}<span class="sr-only">+</span><span class="tion"><span class="num">{NUM}</span><span class="sr-only">/</span><span class="den">{DEN}</span></span></span>', -- 1+2//3 |
|||
-- 1+2//3 : signed_wholenumber, numerator, denominator |
|||
style = 'sfrac', |
|||
'<span class="sfrac nowrap" style="display:inline-block; vertical-align:-0.5em; font-size:85%%; text-align:center;"><span style="display:block; line-height:1em; padding:0 0.1em;">%s%s</span><span class="visualhide">/</span><span style="display:block; line-height:1em; padding:0 0.1em; border-top:1px solid;">%s</span></span>', |
|||
'<span class="sfrac nowrap">%s<span class="visualhide"> </span><span style="display:inline-block; vertical-align:-0.5em; font-size:85%%; text-align:center;"><span style="display:block; line-height:1em; padding:0 0.1em;">%s</span><span class="visualhide">/</span><span style="display:block; line-height:1em; padding:0 0.1em; border-top:1px solid;">%s</span></span></span>', |
|||
}, |
}, |
||
} |
} |
||
Line 1,092: | Line 1,163: | ||
wholestr = nil |
wholestr = nil |
||
end |
end |
||
local substitute = { |
|||
if wholestr then |
|||
SIGN = negative and MINUS or '', |
|||
local decorated = with_separator(parms, wholestr) |
|||
WHOLE = wholestr and with_separator(parms, wholestr), |
|||
if negative then |
|||
NUM = from_en(numstr), |
|||
decorated = MINUS .. decorated |
|||
DEN = from_en(denstr), |
|||
end |
|||
} |
|||
local fmt = fracfmt[style][2] |
|||
wikitext = fracfmt[style][wholestr and 2 or 1]:gsub('{(%u+)}', substitute) |
|||
else |
|||
local sign = negative and MINUS or '' |
|||
wikitext = format(fracfmt[style][1], sign, from_en(numstr), from_en(denstr)) |
|||
end |
|||
if do_spell then |
if do_spell then |
||
if negative then |
if negative then |
||
Line 1,111: | Line 1,178: | ||
end |
end |
||
end |
end |
||
local s = spell_number(parms, inout, wholestr, numstr, denstr) |
|||
if s then |
|||
return s |
|||
end |
|||
end |
end |
||
add_style(parms, fracfmt[style].style) |
|||
return wikitext |
return wikitext |
||
end |
end |
||
Line 1,279: | Line 1,350: | ||
-- with the hands unit (not worth adding code to enforce that). |
-- with the hands unit (not worth adding code to enforce that). |
||
------------------------------------------------------------------------ |
------------------------------------------------------------------------ |
||
local numstr, whole |
|||
local leading_plus, prefix, numstr, slashes, denstr = |
local leading_plus, prefix, numstr, slashes, denstr = |
||
text:match('^%s*(%+?)%s*(.-)%s*(%d+)%s*(/+)%s*(%d+)%s*$') |
text:match('^%s*(%+?)%s*(.-)%s*(%d+)%s*(/+)%s*(%d+)%s*$') |
||
Line 1,293: | Line 1,363: | ||
return nil |
return nil |
||
end |
end |
||
local wholestr |
local whole, wholestr |
||
if prefix == '' then |
if prefix == '' then |
||
wholestr = '' |
wholestr = '' |
||
Line 1,332: | Line 1,402: | ||
-- Before processing, the input text is cleaned: |
-- Before processing, the input text is cleaned: |
||
-- * Any thousand separators (valid or not) are removed. |
-- * Any thousand separators (valid or not) are removed. |
||
-- * Any sign |
-- * Any sign is replaced with '-' (if negative) or '' (otherwise). |
||
-- '-' (if negative) or '' (otherwise). |
|||
-- That replaces Unicode minus with '-'. |
-- That replaces Unicode minus with '-'. |
||
-- If successful, the returned info table contains named fields: |
-- If successful, the returned info table contains named fields: |
||
Line 1,359: | Line 1,428: | ||
while #remainder > 0 do |
while #remainder > 0 do |
||
local ref, spaces |
local ref, spaces |
||
ref, spaces, remainder = remainder:match('^(\ |
ref, spaces, remainder = remainder:match('^(\127[^\127]*UNIQ[^\127]*%-ref[^\127]*\127)(%s*)(.*)') |
||
if ref then |
if ref then |
||
table.insert(refs, ref) |
table.insert(refs, ref) |
||
Line 1,393: | Line 1,462: | ||
local valstr |
local valstr |
||
for _, prefix in ipairs({ '-', MINUS, '−' }) do |
for _, prefix in ipairs({ '-', MINUS, '−' }) do |
||
-- Including '-' |
-- Including '-' sets isnegative in case input is a fraction like '-2-3/4'. |
||
-- It also sets isnegative in case input is a fraction like '-2-3/4'. |
|||
local plen = #prefix |
local plen = #prefix |
||
if clean:sub(1, plen) == prefix then |
if clean:sub(1, plen) == prefix then |
||
valstr = clean:sub(plen + 1) |
valstr = clean:sub(plen + 1) |
||
if valstr:match('^%s') then -- "- 1" is invalid but "-1 - 1/2" is ok |
|||
return false, { 'cvt_bad_num', text } |
|||
end |
|||
break |
break |
||
end |
end |
||
Line 1,460: | Line 1,531: | ||
end |
end |
||
end |
end |
||
local altvalue = altvalue or value |
|||
if isnegative and (value ~= 0) then |
if isnegative and (value ~= 0) then |
||
value = -value |
value = -value |
||
altvalue = -altvalue |
altvalue = -(altvalue or value) |
||
end |
end |
||
return true, { |
return true, { |
||
value = value, |
value = value, |
||
altvalue = altvalue, |
altvalue = altvalue or value, |
||
singular = singular, |
singular = singular, |
||
clean = clean, |
clean = clean, |
||
Line 1,481: | Line 1,551: | ||
-- v = value of text (text is a number) |
-- v = value of text (text is a number) |
||
-- f = true if value is an integer |
-- f = true if value is an integer |
||
-- Input can use en digits or digits in local language, |
-- Input can use en digits or digits in local language or separators, |
||
-- but |
-- but no Unicode minus, and no fraction. |
||
if text then |
if text then |
||
local number = tonumber(to_en(text)) |
local number = tonumber(to_en(text)) |
||
if number then |
if number then |
||
local |
local _, fracpart = math.modf(number) |
||
return number, (fracpart == 0) |
return number, (fracpart == 0) |
||
end |
end |
||
Line 1,564: | Line 1,634: | ||
-- p2 is text to insert before the output unit |
-- p2 is text to insert before the output unit |
||
-- p1 or p2 may be nil to mean "no preunit" |
-- p1 or p2 may be nil to mean "no preunit" |
||
-- Using '+ |
-- Using '+' gives output like "5+ feet" (no space before, but space after). |
||
local function withspace(text, |
local function withspace(text, wantboth) |
||
-- |
-- Return text with space before and, if wantboth, after. |
||
-- However, no space is |
-- However, no space is added if there is a space or ' ' or '-' |
||
-- |
-- at that position ('-' is for adjectival text). |
||
-- There is also no space if text starts with '&' |
|||
local current = text:sub(i, i) |
|||
-- (e.g. '°' would display a degree symbol with no preceding space). |
|||
if current == ' ' or current == '-' then |
|||
local char = text:sub(1, 1) |
|||
if char == '&' then |
|||
return text -- an html entity can be used to specify the exact display |
|||
end |
end |
||
if |
if not (char == ' ' or char == '-' or char == '+') then |
||
text = ' ' .. text |
|||
else |
|||
current = text:sub(-6, -1) |
|||
end |
end |
||
if |
if wantboth then |
||
char = text:sub(-1, -1) |
|||
if not (char == ' ' or char == '-' or text:sub(-6, -1) == ' ') then |
|||
end |
|||
text = text .. ' ' |
|||
end |
|||
return ' ' .. text |
|||
end |
end |
||
return text |
return text |
||
end |
end |
||
local PLUS = '+ ' |
|||
preunit1 = preunit1 or '' |
preunit1 = preunit1 or '' |
||
local trim1 = strip(preunit1) |
local trim1 = strip(preunit1) |
||
Line 1,592: | Line 1,663: | ||
return nil |
return nil |
||
end |
end |
||
if trim1 == '+' then |
|||
return withspace(withspace(preunit1, 1), -1) |
|||
return PLUS |
|||
end |
|||
return withspace(preunit1, true) |
|||
end |
end |
||
preunit1 = withspace(preunit1) |
|||
preunit2 = preunit2 or '' |
preunit2 = preunit2 or '' |
||
local trim2 = strip(preunit2) |
local trim2 = strip(preunit2) |
||
if trim1 == ' |
if trim1 == '+' then |
||
if trim2 == '' or trim2 == '+' then |
|||
return nil, nil |
|||
return PLUS, PLUS |
|||
end |
|||
end |
|||
if trim1 ~= '+' then |
|||
preunit1 = |
preunit1 = PLUS |
||
end |
end |
||
if trim2 == ' |
if trim2 == '' then |
||
if trim1 == '' then |
|||
return nil, nil |
|||
elseif trim2 == '' then |
|||
end |
|||
preunit2 = preunit1 |
preunit2 = preunit1 |
||
elseif trim2 |
elseif trim2 == '+' then |
||
preunit2 = |
preunit2 = PLUS |
||
elseif trim2 == ' ' then -- trick to make preunit2 empty |
|||
preunit2 = nil |
|||
else |
|||
preunit2 = withspace(preunit2) |
|||
end |
end |
||
return preunit1, preunit2 |
return preunit1, preunit2 |
||
end |
end |
||
local function range_text(range, want_name, parms, before, after, inout) |
local function range_text(range, want_name, parms, before, after, inout, options) |
||
-- Return before .. rtext .. after |
-- Return before .. rtext .. after |
||
-- where rtext is the text that separates two values in a range. |
-- where rtext is the text that separates two values in a range. |
||
local rtext, adj_text, exception |
local rtext, adj_text, exception |
||
options = options or {} |
|||
if type(range) == 'table' then |
if type(range) == 'table' then |
||
-- Table must specify range text for ('off' and 'on') or ('input' and 'output'), |
-- Table must specify range text for ('off' and 'on') or ('input' and 'output'), |
||
Line 1,632: | Line 1,713: | ||
end |
end |
||
end |
end |
||
if rtext == '–' and after:sub(1, #MINUS) == MINUS then |
if rtext == '–' and (options.spaced or after:sub(1, #MINUS) == MINUS) then |
||
rtext = ' – ' |
rtext = ' – ' |
||
end |
end |
||
Line 1,639: | Line 1,720: | ||
local function get_composite(parms, iparm, in_unit_table) |
local function get_composite(parms, iparm, in_unit_table) |
||
-- Look for a composite input unit. For example, |
-- Look for a composite input unit. For example, {{convert|1|yd|2|ft|3|in}} |
||
-- would result in a call to this function with |
-- would result in a call to this function with |
||
-- iparm = 3 (parms[iparm] = "2", just after the first unit) |
-- iparm = 3 (parms[iparm] = "2", just after the first unit) |
||
Line 1,718: | Line 1,799: | ||
kv_pairs.sing = nil |
kv_pairs.sing = nil |
||
end |
end |
||
kv_pairs.comma = kv_pairs.comma or config.comma -- for plwiki who want default comma=5 |
|||
for loc_name, loc_value in pairs(kv_pairs) do |
for loc_name, loc_value in pairs(kv_pairs) do |
||
local en_name = text_code.en_option_name[loc_name] |
local en_name = text_code.en_option_name[loc_name] |
||
if en_name then |
if en_name then |
||
local en_value |
local en_value = text_code.en_option_value[en_name] |
||
if |
if en_value == 'INTEGER' then -- altitude_ft, altitude_m, frac, sigfig |
||
en_value = nil |
|||
if loc_value == '' then |
if loc_value == '' then |
||
add_warning(parms, 2, 'cvt_empty_option', loc_name) |
add_warning(parms, 2, 'cvt_empty_option', loc_name) |
||
elseif en_name == '$' then |
|||
-- Value should be a single character like "€" for the euro currency symbol, but anything is accepted. |
|||
currency_text = (loc_value == 'euro') and '€' or loc_value |
|||
else |
else |
||
local minimum |
local minimum |
||
local number, is_integer = get_number(loc_value) |
local number, is_integer = get_number(loc_value) |
||
if en_name == ' |
if en_name == 'sigfig' then |
||
minimum = 1 |
|||
elseif en_name == 'frac' then |
|||
minimum = 2 |
minimum = 2 |
||
if number and number < 0 then |
if number and number < 0 then |
||
Line 1,738: | Line 1,820: | ||
end |
end |
||
else |
else |
||
minimum = |
minimum = -1e6 |
||
end |
end |
||
if number and is_integer and number >= minimum then |
if number and is_integer and number >= minimum then |
||
en_value = number |
en_value = number |
||
else |
else |
||
local m |
|||
add_warning(parms, 1, (en_name == 'frac' and 'cvt_bad_frac' or 'cvt_bad_sigfig'), loc_value) |
|||
if en_name == 'frac' then |
|||
m = 'cvt_bad_frac' |
|||
elseif en_name == 'sigfig' then |
|||
m = 'cvt_bad_sigfig' |
|||
else |
|||
m = 'cvt_bad_altitude' |
|||
end |
|||
add_warning(parms, 1, m, loc_name .. '=' .. loc_value) |
|||
end |
end |
||
end |
end |
||
elseif |
elseif en_value == 'TEXT' then -- $, input, qid, qual, stylein, styleout, tracking |
||
en_value = loc_value -- accept user text with no validation |
en_value = loc_value ~= '' and loc_value or nil -- accept non-empty user text with no validation |
||
if not en_value and (en_name == '$' or en_name == 'qid' or en_name == 'qual') then |
|||
add_warning(parms, 2, 'cvt_empty_option', loc_name) |
|||
elseif en_name == '$' then |
|||
-- Value should be a single character like "€" for the euro currency symbol, but anything is accepted. |
|||
currency_text = (loc_value == 'euro') and '€' or loc_value |
|||
elseif en_name == 'input' then |
|||
-- May have something like {{convert|input=}} (empty input) if source is an infobox |
|||
-- with optional fields. In that case, want to output nothing rather than an error. |
|||
parms.input_text = loc_value -- keep input because parms.input is nil if loc_value == '' |
|||
end |
|||
else |
else |
||
en_value = |
en_value = en_value[loc_value] |
||
if en_value and en_value:sub(-1) == '?' then |
if en_value and en_value:sub(-1) == '?' then |
||
en_value = en_value:sub(1, -2) |
en_value = en_value:sub(1, -2) |
||
Line 1,796: | Line 1,896: | ||
end |
end |
||
if parms.abbr then |
if parms.abbr then |
||
if parms.abbr == 'unit' then |
|||
parms.abbr = 'on' |
|||
parms.number_word = true |
|||
end |
|||
parms.abbr_org = parms.abbr -- original abbr, before any flip |
parms.abbr_org = parms.abbr -- original abbr, before any flip |
||
elseif parms.opt_hand_hh then |
elseif parms.opt_hand_hh then |
||
Line 1,802: | Line 1,906: | ||
else |
else |
||
parms.abbr = 'out' -- default is to abbreviate output only (use symbol, not name) |
parms.abbr = 'out' -- default is to abbreviate output only (use symbol, not name) |
||
end |
|||
if parms.opt_order_out then |
|||
-- Disable options that do not work in a useful way with order=out. |
|||
parms.opt_flip = nil -- override adj=flip |
|||
parms.opt_spell_in = nil |
|||
parms.opt_spell_out = nil |
|||
parms.opt_spell_upper = nil |
|||
end |
end |
||
if parms.opt_spell_out and not abbr_entered then |
if parms.opt_spell_out and not abbr_entered then |
||
Line 1,963: | Line 2,074: | ||
local function simple_get_values(parms) |
local function simple_get_values(parms) |
||
-- If input is like "{{convert|valid_value|valid_unit|...}}", |
-- If input is like "{{convert|valid_value|valid_unit|...}}", |
||
-- return true, |
-- return true, i, in_unit, in_unit_table |
||
-- |
-- i = index in parms of what follows valid_unit, if anything. |
||
-- The valid_value is not negative and does not use a fraction, and |
-- The valid_value is not negative and does not use a fraction, and |
||
-- no options requiring further processing of the input are used. |
-- no options requiring further processing of the input are used. |
||
-- Otherwise, return nothing |
-- Otherwise, return nothing or return false, parm1 for caller to interpret. |
||
-- Testing shows this function is successful for 96% of converts in articles, |
-- Testing shows this function is successful for 96% of converts in articles, |
||
-- and that on average it speeds up converts by 8%. |
-- and that on average it speeds up converts by 8%. |
||
if parms.opt_ri or parms.opt_spell_in then return end |
|||
local clean = to_en(strip(parms[1] or ''), parms) |
local clean = to_en(strip(parms[1] or ''), parms) |
||
if #clean > 10 or not clean:match('^[0-9.]+$') then |
if parms.opt_ri or parms.opt_spell_in or #clean > 10 or not clean:match('^[0-9.]+$') then |
||
return false, clean |
|||
end |
|||
local value = tonumber(clean) |
local value = tonumber(clean) |
||
if not value then return end |
if not value then return end |
||
Line 1,989: | Line 2,101: | ||
end |
end |
||
local function |
local function wikidata_call(parms, operation, ...) |
||
-- Return true, s where s is the result of a Wikidata operation, |
|||
-- If successful, return true, parms, unit where |
|||
-- or return false, t where t is an error message table. |
|||
local function worker(...) |
|||
wikidata_code = wikidata_code or require(wikidata_module) |
|||
wikidata_data = wikidata_data or mw.loadData(wikidata_data_module) |
|||
return wikidata_code[operation](wikidata_data, ...) |
|||
end |
|||
local success, status, result = pcall(worker, ...) |
|||
if success then |
|||
return status, result |
|||
end |
|||
if parms.opt_sortable_debug then |
|||
-- Use debug=yes to crash if an error while accessing Wikidata. |
|||
error('Error accessing Wikidata: ' .. status, 0) |
|||
end |
|||
return false, { 'cvt_wd_fail' } |
|||
end |
|||
local function get_parms(parms, args) |
|||
-- If successful, update parms and return true, unit where |
|||
-- parms is a table of all arguments passed to the template |
-- parms is a table of all arguments passed to the template |
||
-- converted to named arguments, and |
-- converted to named arguments, and |
||
-- unit is the input unit table; |
-- unit is the input unit table; |
||
-- or return false, t where t is an error message table. |
-- or return false, t where t is an error message table. |
||
-- For special processing (not a convert), can also return |
|||
-- true, wikitext where wikitext is the final result. |
|||
-- The returned input unit table may be for a fake unit using the specified |
-- The returned input unit table may be for a fake unit using the specified |
||
-- unit code as the symbol and name, and with bad_mcode = message code table. |
-- unit code as the symbol and name, and with bad_mcode = message code table. |
||
Line 2,001: | Line 2,134: | ||
-- whitespace entered in the template, and whitespace is used by some |
-- whitespace entered in the template, and whitespace is used by some |
||
-- parameters (example: the numbered parameters associated with "disp=x"). |
-- parameters (example: the numbered parameters associated with "disp=x"). |
||
local parms = {} -- arguments passed to template, after translation |
|||
local kv_pairs = {} -- table of input key:value pairs where key is a name; needed because cannot iterate parms and add new fields to it |
local kv_pairs = {} -- table of input key:value pairs where key is a name; needed because cannot iterate parms and add new fields to it |
||
for k, v in pairs(args) do |
for k, v in pairs(args) do |
||
Line 2,009: | Line 2,141: | ||
kv_pairs[k] = v |
kv_pairs[k] = v |
||
end |
end |
||
end |
|||
if parms.test == 'wikidata' then |
|||
local ulookup = function (ucode) |
|||
-- Use empty table for parms so it does not accumulate results when used repeatedly. |
|||
return lookup({}, ucode, 'no_combination') |
|||
end |
|||
return wikidata_call(parms, '_listunits', ulookup) |
|||
end |
end |
||
local success, msg = translate_parms(parms, kv_pairs) |
local success, msg = translate_parms(parms, kv_pairs) |
||
if not success then return false, msg end |
if not success then return false, msg end |
||
if parms.input then |
|||
success, msg = wikidata_call(parms, '_adjustparameters', parms, 1) |
|||
if not success then return false, msg end |
|||
end |
|||
local success, i, in_unit, in_unit_table = simple_get_values(parms) |
local success, i, in_unit, in_unit_table = simple_get_values(parms) |
||
if not success then |
if not success then |
||
if type(i) == 'string' and i:match('^NNN+$') then |
|||
-- Some infoboxes have examples like {{convert|NNN|m}} (3 or more "N"). |
|||
-- Output an empty string for these. |
|||
return false, { 'cvt_no_output' } |
|||
end |
|||
local valinfo |
local valinfo |
||
success, valinfo, i = get_values(parms) |
success, valinfo, i = get_values(parms) |
||
Line 2,021: | Line 2,169: | ||
success, in_unit_table = lookup(parms, in_unit, 'no_combination') |
success, in_unit_table = lookup(parms, in_unit, 'no_combination') |
||
if not success then |
if not success then |
||
in_unit = in_unit or '' |
|||
in_unit = '' |
|||
end |
|||
if parms.opt_ignore_error then -- display given unit code with no error (for use with {{val}}) |
if parms.opt_ignore_error then -- display given unit code with no error (for use with {{val}}) |
||
in_unit_table = '' -- suppress error message and prevent processing of output unit |
in_unit_table = '' -- suppress error message and prevent processing of output unit |
||
end |
end |
||
in_unit_table = setmetatable({ |
in_unit_table = setmetatable({ |
||
symbol = in_unit, name2 = in_unit, utype = in_unit, |
|||
scale = 1, default = '', defkey = '', linkey = '', |
|||
bad_mcode = in_unit_table }, unit_mt) |
|||
end |
end |
||
in_unit_table.valinfo = valinfo |
in_unit_table.valinfo = valinfo |
||
Line 2,054: | Line 2,201: | ||
end |
end |
||
if in_unit_table.builtin == 'mach' then |
if in_unit_table.builtin == 'mach' then |
||
-- As with old template, a number following Mach as the input unit is the altitude |
-- As with old template, a number following Mach as the input unit is the altitude. |
||
-- That is deprecated: should use altitude_ft=NUMBER or altitude_m=NUMBER. |
|||
-- and there is no way to specify an altitude for the output unit. |
|||
local success, info |
|||
-- Could put more code in this function to get any output unit and check for |
|||
success = tonumber(parms[i]) -- this will often work and will give correct result for values like 2e4 without forcing output scientific notation |
|||
-- an altitude following that unit. |
|||
if success then |
|||
local success, info = extract_number(parms, parms[i], false, true) |
|||
info = { value = success } |
|||
else |
|||
success, info = extract_number(parms, parms[i], false, true) |
|||
end |
|||
if success then |
if success then |
||
i = i + 1 |
i = i + 1 |
||
Line 2,064: | Line 2,215: | ||
end |
end |
||
end |
end |
||
local |
local word = strip(parms[i]) |
||
i = i + 1 |
i = i + 1 |
||
local precision, is_bad_precision |
local precision, is_bad_precision |
||
Line 2,079: | Line 2,230: | ||
end |
end |
||
end |
end |
||
if not set_precision( |
if word and not set_precision(word) then |
||
parms.out_unit = |
parms.out_unit = parms.out_unit or word |
||
if set_precision(strip(parms[i])) then |
if set_precision(strip(parms[i])) then |
||
i = i + 1 |
i = i + 1 |
||
Line 2,086: | Line 2,237: | ||
end |
end |
||
if parms.opt_adj_mid then |
if parms.opt_adj_mid then |
||
word = parms[i] |
|||
i = i + 1 |
i = i + 1 |
||
if |
if word then -- mid-text words |
||
if |
if word:sub(1, 1) == '-' then |
||
parms.mid = |
parms.mid = word |
||
else |
else |
||
parms.mid = ' ' .. |
parms.mid = ' ' .. word |
||
end |
end |
||
end |
end |
||
Line 2,132: | Line 2,283: | ||
parms.precision = precision |
parms.precision = precision |
||
end |
end |
||
for j = i, i + 3 do |
|||
return true, parms, in_unit_table |
|||
local parm = parms[j] -- warn if find a non-empty extraneous parameter |
|||
if parm and parm:match('%S') then |
|||
add_warning(parms, 1, 'cvt_unknown_option', parm) |
|||
break |
|||
end |
|||
end |
|||
return true, in_unit_table |
|||
end |
end |
||
Line 2,168: | Line 2,326: | ||
local fudge = 1e-14 -- {{Order of magnitude}} adds this, so we do too |
local fudge = 1e-14 -- {{Order of magnitude}} adds this, so we do too |
||
local prec, minprec, adjust |
local prec, minprec, adjust |
||
local utype = out_current.utype |
|||
local subunit_ignore_trailing_zero |
local subunit_ignore_trailing_zero |
||
local subunit_more_precision -- kludge for "in" used in input like "|2|ft|6|in" |
local subunit_more_precision -- kludge for "in" used in input like "|2|ft|6|in" |
||
Line 2,280: | Line 2,437: | ||
end |
end |
||
if in_builtin == 'mach' or out_builtin == 'mach' then |
if in_builtin == 'mach' or out_builtin == 'mach' then |
||
-- Should check that only one altitude is given but am planning to remove |
|||
local adjust |
|||
-- in_current.altitude (which can only occur when Mach is the input unit), |
|||
-- and out_current.altitude cannot occur. |
|||
local alt = parms.altitude_ft or in_current.altitude |
|||
if not alt and parms.altitude_m then |
|||
alt = parms.altitude_m / 0.3048 -- 1 ft = 0.3048 m |
|||
end |
|||
local spd = speed_of_sound(alt) |
|||
if in_builtin == 'mach' then |
if in_builtin == 'mach' then |
||
inscale = |
inscale = spd |
||
return invalue * (inscale / outscale) |
|||
else |
|||
outscale = speed_of_sound(out_current.altitude) |
|||
adjust = 0.1 / inscale |
|||
end |
end |
||
outscale = spd |
|||
local adjust = 0.1 / inscale |
|||
return true, { |
return true, { |
||
outvalue = invalue * (inscale / outscale), |
outvalue = invalue * (inscale / outscale), |
||
Line 2,406: | Line 2,569: | ||
end |
end |
||
local sortspan |
local sortspan |
||
if sortkey and |
if sortkey and not parms.table_align then |
||
sortspan = parms.opt_sortable_debug and |
sortspan = parms.opt_sortable_debug and |
||
'<span style="border:1px solid |
'<span data-sort-value="' .. sortkey .. '♠"><span style="border:1px solid">' .. sortkey .. '♠</span></span>' or |
||
'<span |
'<span data-sort-value="' .. sortkey .. '♠"></span>' |
||
parms.join_before = sortspan |
parms.join_before = sortspan |
||
end |
end |
||
if parms.table_align then |
if parms.table_align then |
||
local sort |
|||
if sortkey then |
|||
sort = ' data-sort-value="' .. sortkey .. '"' |
|||
if parms.opt_sortable_debug then |
|||
parms.join_before = '<span style="border:1px solid">' .. sortkey .. '</span>' |
|||
end |
|||
else |
|||
sort = '' |
|||
end |
|||
local style = 'style="text-align:' .. parms.table_align .. ';' |
local style = 'style="text-align:' .. parms.table_align .. ';' |
||
local sort = sortkey and ' data-sort-value="' .. sortkey .. '"' or '' |
|||
local joins = {} |
local joins = {} |
||
for i = 1, 2 do |
for i = 1, 2 do |
||
Line 2,433: | Line 2,604: | ||
-- is "1", or like "1.00", or is a fraction with value < 1; |
-- is "1", or like "1.00", or is a fraction with value < 1; |
||
-- (and more fields shown below, and a calculated 'absvalue' field). |
-- (and more fields shown below, and a calculated 'absvalue' field). |
||
-- or return true, nil if no value specified; |
|||
-- or return false, t where t is an error message table. |
-- or return false, t where t is an error message table. |
||
-- Input info.clean uses en digits (it has been translated, if necessary). |
-- Input info.clean uses en digits (it has been translated, if necessary). |
||
-- Output show uses en or non-en digits as appropriate, or can be spelled. |
-- Output show uses en or non-en digits as appropriate, or can be spelled. |
||
local invalue |
|||
if info then |
|||
invalue = info.value |
|||
if in_current.builtin == 'hand' then |
|||
invalue = info.altvalue |
|||
end |
|||
end |
|||
if invalue == nil or invalue == '' then |
|||
return true, nil |
|||
end |
|||
if out_current.builtin == 'hand' then |
if out_current.builtin == 'hand' then |
||
return cvt_to_hand(parms, info, in_current, out_current) |
return cvt_to_hand(parms, info, in_current, out_current) |
||
end |
end |
||
local invalue = in_current.builtin == 'hand' and info.altvalue or info.value |
|||
local outvalue, extra = convert(parms, invalue, info, in_current, out_current) |
local outvalue, extra = convert(parms, invalue, info, in_current, out_current) |
||
if parms.need_table_or_sort then |
if parms.need_table_or_sort then |
||
Line 2,468: | Line 2,629: | ||
outvalue = -outvalue |
outvalue = -outvalue |
||
end |
end |
||
local |
local precision, show, exponent |
||
local denominator = out_current.frac |
local denominator = out_current.frac |
||
if denominator then |
if denominator then |
||
Line 2,489: | Line 2,650: | ||
show = format('%.0f', floor((outvalue / n) + 0.5) * n) |
show = format('%.0f', floor((outvalue / n) + 0.5) * n) |
||
end |
end |
||
elseif in_current.builtin == 'mach' then |
|||
local sigfig = info.clean:gsub('^[0.]+', ''):gsub('%.', ''):len() + 1 |
|||
show, exponent = make_sigfig(outvalue, sigfig) |
|||
else |
else |
||
local inclean = info.clean |
local inclean = info.clean |
||
Line 2,720: | Line 2,884: | ||
local linked_pages -- to record linked pages so will not link to the same page more than once |
local linked_pages -- to record linked pages so will not link to the same page more than once |
||
local function |
local function unlink(unit_table) |
||
-- Forget that the given unit has previously been linked (if it has). |
|||
-- That is needed when processing a range of inputs or outputs when an id |
|||
-- for the first range value may have been evaluated, but only an id for |
|||
-- the last value is displayed, and that id may need to be linked. |
|||
linked_pages[unit_table.unitcode or unit_table] = nil |
|||
end |
|||
local function make_link(link, id, unit_table) |
|||
-- Return wikilink "[[link|id]]", possibly abbreviated as in examples: |
-- Return wikilink "[[link|id]]", possibly abbreviated as in examples: |
||
-- [[Mile|mile]] --> [[mile]] |
-- [[Mile|mile]] --> [[mile]] |
||
Line 2,727: | Line 2,899: | ||
-- * no link given (so caller does not need to check if a link was defined); or |
-- * no link given (so caller does not need to check if a link was defined); or |
||
-- * link has previously been used during the current convert (to avoid overlinking). |
-- * link has previously been used during the current convert (to avoid overlinking). |
||
local link_key |
|||
-- Linking with a unit uses the unit table as the link key, which fails to detect |
|||
if unit_table then |
|||
-- overlinking for conversions like the following (each links "mile" twice): |
|||
link_key = unit_table.unitcode or unit_table |
|||
-- {{convert|1|impgal/mi|USgal/mi|lk=on}} |
|||
else |
|||
-- {{convert|1|l/km|impgal/mi USgal/mi|lk=on}} |
|||
link_key = link |
|||
link_key = link_key or link -- use key if given (the key, but not the link, may be known when need to cancel a link record) |
|||
end |
|||
if not link or link == '' or linked_pages[link_key] then |
if not link or link == '' or linked_pages[link_key] then |
||
return id |
return id |
||
Line 2,775: | Line 2,948: | ||
else |
else |
||
i = 3 |
i = 3 |
||
end |
|||
if i > 1 and varname == 'pl' then |
|||
i = i - 1 |
|||
end |
end |
||
vname = split(unit_table.varname, '!')[i] |
vname = split(unit_table.varname, '!')[i] |
||
Line 2,805: | Line 2,981: | ||
local per = unit_table.per |
local per = unit_table.per |
||
if per then |
if per then |
||
local paren1, paren2 = '', '' -- possible parentheses around bottom unit |
|||
local unit1 = per[1] -- top unit_table, or nil |
local unit1 = per[1] -- top unit_table, or nil |
||
local unit2 = per[2] -- bottom unit_table |
local unit2 = per[2] -- bottom unit_table |
||
Line 2,816: | Line 2,993: | ||
return symbol -- for exceptions that have the symbol built-in |
return symbol -- for exceptions that have the symbol built-in |
||
end |
end |
||
end |
|||
if (unit2.symbol):find('⋅', 1, true) then |
|||
paren1, paren2 = '(', ')' |
|||
end |
end |
||
end |
end |
||
Line 2,855: | Line 3,035: | ||
unit_table.sep = '' |
unit_table.sep = '' |
||
end |
end |
||
return result .. linked_id(parms, unit2, key_id2, want_link, '1') |
return result .. paren1 .. linked_id(parms, unit2, key_id2, want_link, '1') .. paren2 |
||
end |
end |
||
if multiplier then |
if multiplier then |
||
Line 2,936: | Line 3,116: | ||
local abbr_org = parms.abbr_org |
local abbr_org = parms.abbr_org |
||
local adjectival = parms.opt_adjectival |
local adjectival = parms.opt_adjectival |
||
local disp = parms.disp |
|||
local lk = parms.lk |
local lk = parms.lk |
||
local want_link = (lk == 'on' or lk == inout) |
local want_link = (lk == 'on' or lk == inout) |
||
Line 3,021: | Line 3,200: | ||
local inout = unit_table.inout |
local inout = unit_table.inout |
||
local abbr = parms.abbr |
local abbr = parms.abbr |
||
if abbr == 'on' or abbr == inout then |
if (abbr == 'on' or abbr == inout) and not parms.number_word then |
||
info.show = info.show .. |
info.show = info.show .. |
||
'<span style="margin-left:0.2em">×<span style="margin-left:0.1em">' .. |
'<span style="margin-left:0.2em">×<span style="margin-left:0.1em">' .. |
||
from_en('10') .. |
from_en('10') .. |
||
'</span></span><s style="display:none">^</s><sup>' .. |
'</span></span><s style="display:none">^</s><sup>' .. |
||
from_en(tostring(engscale.exponent)) .. '</sup>' |
from_en(tostring(engscale.exponent)) .. '</sup>' |
||
elseif number_word then |
elseif number_word then |
||
local number_id |
local number_id |
||
Line 3,083: | Line 3,262: | ||
return preunit .. id1 |
return preunit .. id1 |
||
end |
end |
||
if parms.opt_also_symbol and not composite then |
if parms.opt_also_symbol and not composite and not parms.opt_flip then |
||
local join1 = parms.joins[1] |
local join1 = parms.joins[1] |
||
if join1 == ' (' or join1 == ' [' then |
if join1 == ' (' or join1 == ' [' then |
||
Line 3,097: | Line 3,276: | ||
-- For simplicity and because more not needed, handle one range item only. |
-- For simplicity and because more not needed, handle one range item only. |
||
local prefix2 = make_id(parms, 2, first_unit) .. ' ' |
local prefix2 = make_id(parms, 2, first_unit) .. ' ' |
||
result = range_text(range[1], want_name, parms, result, prefix2 .. valinfo[2].show, 'in') |
result = range_text(range[1], want_name, parms, result, prefix2 .. valinfo[2].show, 'in', {spaced=true}) |
||
end |
end |
||
return preunit .. result |
return preunit .. result |
||
Line 3,128: | Line 3,307: | ||
local range = parms.range |
local range = parms.range |
||
if range and not add_unit then |
if range and not add_unit then |
||
unlink(first_unit) |
|||
linked_pages[first_unit] = nil -- so the final and only id will be linked, if wanted |
|||
end |
end |
||
local id = range and make_id(parms, range.n + 1, first_unit) or id1 |
local id = range and make_id(parms, range.n + 1, first_unit) or id1 |
||
Line 3,165: | Line 3,344: | ||
-- Processing required for each output unit. |
-- Processing required for each output unit. |
||
-- Return block of text to represent output (value/unit). |
-- Return block of text to represent output (value/unit). |
||
local inout = out_current.inout -- normally 'out' but can be 'in' for order=out |
|||
local id1, want_name = make_id(parms, 1, out_current) |
local id1, want_name = make_id(parms, 1, out_current) |
||
local sep = out_current.sep -- set by make_id |
local sep = out_current.sep -- set by make_id |
||
Line 3,186: | Line 3,366: | ||
if range then |
if range then |
||
-- For simplicity and because more not needed, handle one range item only. |
-- For simplicity and because more not needed, handle one range item only. |
||
result = range_text(range[1], want_name, parms, result, prefix .. valinfo[2].show, |
result = range_text(range[1], want_name, parms, result, prefix .. valinfo[2].show, inout, {spaced=true}) |
||
end |
end |
||
return preunit .. result |
return preunit .. result |
||
Line 3,195: | Line 3,375: | ||
local range = parms.range |
local range = parms.range |
||
if range and not add_unit then |
if range and not add_unit then |
||
unlink(out_current) |
|||
linked_pages[out_current] = nil -- so the final and only id will be linked, if wanted |
|||
end |
end |
||
local id = range and make_id(parms, range.n + 1, out_current) or id1 |
local id = range and make_id(parms, range.n + 1, out_current) or id1 |
||
local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, |
local extra, was_hyphenated = hyphenated_maybe(parms, want_name, sep, id, inout) |
||
if was_hyphenated then |
if was_hyphenated then |
||
add_unit = false |
add_unit = false |
||
Line 3,219: | Line 3,399: | ||
result = show |
result = show |
||
else |
else |
||
result = range_text(range[i], want_name, parms, result, show, |
result = range_text(range[i], want_name, parms, result, show, inout) |
||
end |
end |
||
end |
end |
||
Line 3,236: | Line 3,416: | ||
-- for a single output (which is not a combination or a multiple); |
-- for a single output (which is not a combination or a multiple); |
||
-- or return false, t where t is an error message table. |
-- or return false, t where t is an error message table. |
||
if parms.opt_order_out and in_unit_table.unitcode == out_unit_table.unitcode then |
|||
out_unit_table.valinfo = collection() |
|||
out_unit_table.valinfo = in_unit_table.valinfo |
|||
local range = parms.range |
|||
else |
|||
for i = 1, (range and (range.n + 1) or 1) do |
|||
out_unit_table.valinfo = collection() |
|||
local success, info = cvtround(parms, in_unit_table.valinfo[i], in_unit_table, out_unit_table) |
|||
for _, v in ipairs(in_unit_table.valinfo) do |
|||
if not success then return false, info end |
|||
local success, info = cvtround(parms, v, in_unit_table, out_unit_table) |
|||
out_unit_table.valinfo:add(info) |
|||
if not success then return false, info end |
|||
out_unit_table.valinfo:add(info) |
|||
end |
|||
end |
end |
||
return true, process_one_output(parms, out_unit_table) |
return true, process_one_output(parms, out_unit_table) |
||
Line 3,250: | Line 3,433: | ||
-- for an output which is a multiple (like 'ftin'); |
-- for an output which is a multiple (like 'ftin'); |
||
-- or return false, t where t is an error message table. |
-- or return false, t where t is an error message table. |
||
local inout = out_unit_table.inout -- normally 'out' but can be 'in' for order=out |
|||
local multiple = out_unit_table.multiple -- table of scaling factors (will not be nil) |
local multiple = out_unit_table.multiple -- table of scaling factors (will not be nil) |
||
local combos = out_unit_table.combination -- table of unit tables (will not be nil) |
local combos = out_unit_table.combination -- table of unit tables (will not be nil) |
||
Line 3,256: | Line 3,440: | ||
local disp = parms.disp |
local disp = parms.disp |
||
local want_name = (abbr_org == nil and (disp == 'or' or disp == 'slash')) or |
local want_name = (abbr_org == nil and (disp == 'or' or disp == 'slash')) or |
||
not (abbr == 'on' or abbr == |
not (abbr == 'on' or abbr == inout or abbr == 'mos') |
||
local want_link = (parms.lk == 'on' or parms.lk == |
local want_link = (parms.lk == 'on' or parms.lk == inout) |
||
local mid = parms.opt_flip and parms.mid or '' |
local mid = parms.opt_flip and parms.mid or '' |
||
local sep1 = ' ' |
local sep1 = ' ' |
||
Line 3,273: | Line 3,457: | ||
local tfrac, thisvalue, strforce |
local tfrac, thisvalue, strforce |
||
local out_current = combos[i] |
local out_current = combos[i] |
||
out_current.inout = |
out_current.inout = inout |
||
local scale = multiple[i] |
local scale = multiple[i] |
||
if i == 1 then -- least significant unit ('in' from 'ftin') |
if i == 1 then -- least significant unit ('in' from 'ftin') |
||
Line 3,355: | Line 3,539: | ||
end |
end |
||
local strval |
local strval |
||
local |
local spell_inout = (i == #combos or outvalue == 0) and inout or '' -- trick so the last value processed (first displayed) has uppercase, if requested |
||
if strforce and outvalue == 0 then |
if strforce and outvalue == 0 then |
||
sign = '' -- any sign is in strforce |
sign = '' -- any sign is in strforce |
||
Line 3,361: | Line 3,545: | ||
elseif tfrac then |
elseif tfrac then |
||
local wholestr = (thisvalue > 0) and tostring(thisvalue) or nil |
local wholestr = (thisvalue > 0) and tostring(thisvalue) or nil |
||
strval = format_fraction(parms, |
strval = format_fraction(parms, spell_inout, false, wholestr, tfrac.numstr, tfrac.denstr, do_spell) |
||
else |
else |
||
strval = (thisvalue == 0) and from_en('0') or with_separator(parms, format(fmt, thisvalue)) |
strval = (thisvalue == 0) and from_en('0') or with_separator(parms, format(fmt, thisvalue)) |
||
if do_spell then |
if do_spell then |
||
strval = spell_number(parms, |
strval = spell_number(parms, spell_inout, strval) or strval |
||
end |
end |
||
end |
end |
||
Line 3,388: | Line 3,572: | ||
local success, result2 = make_result(valinfo[i+1]) |
local success, result2 = make_result(valinfo[i+1]) |
||
if not success then return false, result2 end |
if not success then return false, result2 end |
||
result = range_text(range[i], want_name, parms, result, result2, |
result = range_text(range[i], want_name, parms, result, result2, inout, {spaced=true}) |
||
end |
end |
||
end |
end |
||
Line 3,395: | Line 3,579: | ||
local function process(parms, in_unit_table, out_unit_table) |
local function process(parms, in_unit_table, out_unit_table) |
||
-- Return true, s where s = final wikitext result, |
-- Return true, s, outunit where s = final wikitext result, |
||
-- or return false, t where t is an error message table. |
-- or return false, t where t is an error message table. |
||
linked_pages = {} |
linked_pages = {} |
||
local success, bad_output |
local success, bad_output |
||
local bad_input_mcode = in_unit_table.bad_mcode -- |
local bad_input_mcode = in_unit_table.bad_mcode -- nil if input unit is a valid convert unit |
||
local invalue1 = in_unit_table.valinfo[1].value |
|||
local out_unit = parms.out_unit |
local out_unit = parms.out_unit |
||
if out_unit == nil or out_unit == '' then |
if out_unit == nil or out_unit == '' or type(out_unit) == 'function' then |
||
if bad_input_mcode or parms.opt_input_unit_only then |
if bad_input_mcode or parms.opt_input_unit_only then |
||
bad_output = '' |
bad_output = '' |
||
else |
else |
||
local getdef = type(out_unit) == 'function' and out_unit or get_default |
|||
success, out_unit = get_default(invalue1, in_unit_table) |
|||
success, out_unit = getdef(in_unit_table.valinfo[1].value, in_unit_table) |
|||
parms.out_unit = out_unit |
parms.out_unit = out_unit |
||
if not success then |
if not success then |
||
Line 3,424: | Line 3,608: | ||
end |
end |
||
end |
end |
||
local lhs, rhs |
|||
local flipped = parms.opt_flip and not bad_input_mcode |
local flipped = parms.opt_flip and not bad_input_mcode |
||
if bad_output then |
|||
local parts = {} |
|||
rhs = (bad_output == '') and '' or message(parms, bad_output) |
|||
for part = 1, 2 do |
|||
elseif parms.opt_input_unit_only then |
|||
-- The LHS (parts[1]) is normally the input, but is the output if flipped. |
|||
rhs = '' |
|||
-- Process LHS first so it will be linked, if wanted. |
|||
else |
|||
-- Linking to the same item is suppressed in the RHS to avoid overlinking. |
|||
local combos -- nil (for 'ft' or 'ftin'), or table of unit tables (for 'm ft') |
|||
if not out_unit_table.multiple then -- nil/false ('ft' or 'm ft'), or table of factors ('ftin') |
|||
parts[part] = process_input(parms, in_unit_table) |
|||
combos = out_unit_table.combination |
|||
elseif bad_output then |
|||
end |
|||
parts[part] = (bad_output == '') and '' or message(bad_output) |
|||
local frac = parms.frac -- nil or denominator of fraction for output values |
|||
else |
|||
if frac then |
|||
local outputs = {} |
|||
-- Apply fraction to the unit (if only one), or to non-SI units (if a combination), |
|||
-- except that if a precision is also specified, the fraction only applies to |
|||
if not out_unit_table.multiple then -- nil/false ('ft' or 'm ft'), or table of factors ('ftin') |
|||
-- the hand unit; that allows the following result: |
|||
combos = out_unit_table.combination |
|||
-- {{convert|156|cm|in hand|1|frac=2}} → 156 centimetres (61.4 in; 15.1½ hands) |
|||
end |
|||
-- However, the following is handled elsewhere as a special case: |
|||
local frac = parms.frac -- nil or denominator of fraction for output values |
|||
-- {{convert|156|cm|hand in|1|frac=2}} → 156 centimetres (15.1½ hands; 61½ in) |
|||
if frac then |
|||
if combos then |
|||
-- Apply fraction to the unit (if only one), or to non-SI units (if a combination), |
|||
local precision = parms.precision |
|||
-- except that if a precision is also specified, the fraction only applies to |
|||
for _, unit in ipairs(combos) do |
|||
if unit.builtin == 'hand' or (not precision and not unit.prefixes) then |
|||
-- {{convert|156|cm|in hand|1|frac=2}} → 156 centimetres (61.4 in; 15.1½ hands) |
|||
unit.frac = frac |
|||
-- However, the following is handled elsewhere as a special case: |
|||
-- {{convert|156|cm|hand in|1|frac=2}} → 156 centimetres (15.1½ hands; 61½ in) |
|||
if combos then |
|||
local precision = parms.precision |
|||
for _, unit in ipairs(combos) do |
|||
if unit.builtin == 'hand' or (not precision and not unit.prefixes) then |
|||
unit.frac = frac |
|||
end |
|||
end |
end |
||
else |
|||
out_unit_table.frac = frac |
|||
end |
end |
||
else |
|||
out_unit_table.frac = frac |
|||
end |
end |
||
end |
|||
local out_first |
|||
local outputs = {} |
|||
local imax = combos and #combos or 1 -- 1 (single unit) or number of unit tables |
|||
local imax = combos and #combos or 1 -- 1 (single unit) or number of unit tables |
|||
for i = 1, imax do |
|||
if imax == 1 then |
|||
local success, item |
|||
parms.opt_order_out = nil -- only useful with an output combination |
|||
local out_current = combos and combos[i] or out_unit_table |
|||
end |
|||
out_current.inout = 'out' |
|||
if not flipped and not parms.opt_order_out then |
|||
-- Process left side first so any duplicate links (from lk=on) are suppressed |
|||
out_first = out_current |
|||
-- on right. Example: {{convert|28|e9pc|e9ly|abbr=off|lk=on}} |
|||
if imax > 1 and out_current.builtin == 'hand' then |
|||
lhs = process_input(parms, in_unit_table) |
|||
out_current.out_next = combos[2] -- built-in hand can influence next unit in a combination |
|||
end |
|||
for i = 1, imax do |
|||
local success, item |
|||
local out_current = combos and combos[i] or out_unit_table |
|||
out_current.inout = 'out' |
|||
if i == 1 then |
|||
if imax > 1 and out_current.builtin == 'hand' then |
|||
out_current.out_next = combos[2] -- built-in hand can influence next unit in a combination |
|||
end |
end |
||
if |
if parms.opt_order_out then |
||
out_current.inout = 'in' |
|||
success, item = make_output_multiple(parms, in_unit_table, out_current) |
|||
else |
|||
success, item = make_output_single(parms, in_unit_table, out_current) |
|||
end |
end |
||
if not success then return false, item end |
|||
table.insert(outputs, item) |
|||
end |
end |
||
if |
if out_current.multiple then |
||
success, item = make_output_multiple(parms, in_unit_table, out_current) |
|||
parts[part] = '' |
|||
else |
else |
||
success, item = make_output_single(parms, in_unit_table, out_current) |
|||
local sep = parms.table_joins and parms.table_joins[2] or parms.join_between |
|||
parts[part] = table.concat(outputs, sep) |
|||
end |
end |
||
if not success then return false, item end |
|||
outputs[i] = item |
|||
end |
|||
if parms.opt_order_out then |
|||
lhs = outputs[1] |
|||
table.remove(outputs, 1) |
|||
end |
|||
local sep = parms.table_joins and parms.table_joins[2] or parms.join_between |
|||
rhs = table.concat(outputs, sep) |
|||
end |
|||
if flipped or not lhs then |
|||
local input = process_input(parms, in_unit_table) |
|||
if flipped then |
|||
lhs = rhs |
|||
rhs = input |
|||
else |
|||
lhs = input |
|||
end |
end |
||
end |
end |
||
if parms.join_before then |
if parms.join_before then |
||
lhs = parms.join_before .. lhs |
|||
end |
end |
||
local wikitext |
local wikitext |
||
if bad_input_mcode then |
if bad_input_mcode then |
||
if bad_input_mcode == '' then |
if bad_input_mcode == '' then |
||
wikitext = |
wikitext = lhs |
||
else |
else |
||
wikitext = |
wikitext = lhs .. message(parms, bad_input_mcode) |
||
end |
end |
||
elseif parms.table_joins then |
elseif parms.table_joins then |
||
wikitext = parms.table_joins[1] .. |
wikitext = parms.table_joins[1] .. lhs .. parms.table_joins[2] .. rhs |
||
else |
else |
||
wikitext = |
wikitext = lhs .. parms.joins[1] .. rhs .. parms.joins[2] |
||
end |
end |
||
if parms.warnings and not bad_input_mcode then |
if parms.warnings and not bad_input_mcode then |
||
wikitext = wikitext .. parms.warnings |
wikitext = wikitext .. parms.warnings |
||
end |
end |
||
return true, wikitext, out_unit_table |
return true, get_styles(parms) .. wikitext, out_unit_table |
||
end |
end |
||
local function main_convert(frame) |
local function main_convert(frame) |
||
-- Do convert, and if needed, do it again with higher default precision. |
-- Do convert, and if needed, do it again with higher default precision. |
||
local parms = { frame = frame } -- will hold template arguments, after translation |
|||
set_config(frame.args) |
set_config(frame.args) |
||
local result, |
local success, result = get_parms(parms, frame:getParent().args) |
||
local success, parms, in_unit_table = get_parms(frame:getParent().args) |
|||
if success then |
if success then |
||
if type(result) ~= 'table' then |
|||
for i = 1, 2 do -- use counter so cannot get stuck repeating convert |
|||
return tostring(result) |
|||
end |
|||
local in_unit_table = result |
|||
local out_unit_table |
|||
for _ = 1, 2 do -- use counter so cannot get stuck repeating convert |
|||
success, result, out_unit_table = process(parms, in_unit_table, out_unit_table) |
success, result, out_unit_table = process(parms, in_unit_table, out_unit_table) |
||
if success and parms.do_convert_again then |
if success and parms.do_convert_again then |
||
Line 3,522: | Line 3,724: | ||
end |
end |
||
end |
end |
||
else |
|||
result = parms |
|||
end |
end |
||
-- If input=x gives a problem, the result should be just the user input |
|||
if success then |
|||
-- (if x is a property like P123 it has been replaced with ''). |
|||
return result |
|||
-- An unknown input unit would display the input and an error message |
|||
-- with success == true at this point. |
|||
-- Also, can have success == false with a message that outputs an empty string. |
|||
if parms.input_text then |
|||
if success and not parms.have_problem then |
|||
return result |
|||
end |
|||
local cat |
|||
if parms.tracking then |
|||
-- Add a tracking category using the given text as the category sort key. |
|||
-- There is currently only one type of tracking, but in principle multiple |
|||
-- items could be tracked, using different sort keys for convenience. |
|||
cat = wanted_category('tracking', parms.tracking) |
|||
end |
|||
return parms.input_text .. (cat or '') |
|||
end |
end |
||
return message(result) |
return success and result or message(parms, result) |
||
end |
end |
||
Line 3,569: | Line 3,784: | ||
opt_sortable_debug = options.sort == 'debug', |
opt_sortable_debug = options.sort == 'debug', |
||
} |
} |
||
local utable |
|||
if options.si then |
if options.si then |
||
-- Make a dummy table of units (just one unit) for lookup to use. |
-- Make a dummy table of units (just one unit) for lookup to use. |
||
-- This makes lookup recognize any SI prefix in the unitcode. |
-- This makes lookup recognize any SI prefix in the unitcode. |
||
local symbol = options.si[1] or '?' |
local symbol = options.si[1] or '?' |
||
parms.unittable = { [symbol] = { |
|||
_name1 = symbol, |
_name1 = symbol, |
||
_name2 = symbol, |
_name2 = symbol, |
||
Line 3,585: | Line 3,799: | ||
}} |
}} |
||
end |
end |
||
local success, unit_table = lookup(parms, unitcode, 'no_combination' |
local success, unit_table = lookup(parms, unitcode, 'no_combination') |
||
if not success then |
if not success then |
||
unit_table = setmetatable({ |
unit_table = setmetatable({ |
||
symbol = unitcode, name2 = unitcode, utype = unitcode, |
|||
scale = 1, default = '', defkey = '', linkey = '' }, unit_mt) |
|||
utype = "length", scale = 1 }, unit_mt) |
|||
end |
end |
||
local value = tonumber(options.value) or 1 |
local value = tonumber(options.value) or 1 |