Module:Convert: Difference between revisions
From Zoophilia Wiki
Jump to navigationJump to search
meta>WOSlinker testing |
meta>WOSlinker try this |
||
Line 22: | Line 22: | ||
]]-- | ]]-- | ||
local function scaled( | local function scaled(value, in_unit, out_unit) | ||
-- Return scaled value for a simple convert. | -- Return scaled value for a simple convert. | ||
return (value - in_unit[unit.offset]) / in_unit[unit.scale] * out_unit[unit.scale] + out_unit[unit.offset] | |||
end | end | ||
local units = { | local units = { | ||
Line 72: | Line 36: | ||
error(msg:format(unit)) | error(msg:format(unit)) | ||
end | end | ||
return t | return t | ||
end, | end, | ||
['kg'] = {mass, 'lb'}, | utype = 1, | ||
['lb'] = {mass, 'kg'}, | scale = 2, | ||
['m'] = {length, 'ft'}, | offset = 3, | ||
['km'] = {length, 'mi'}, | defaultunit = 4, | ||
['mi'] = {length, 'km'}, | ['kg'] = {'mass', 1, 0, 'lb'}, | ||
['ft'] = {length, 'm'}, | ['lb'] = {'mass', 0.45359237, 0, 'kg'}, | ||
['K'] = {temperature, 'C'}, | ['m'] = {'length', 1, 0, 'ft'}, | ||
['C'] = {temperature, 'F'}, | ['km'] = {'length', 1000, 0, 'mi'}, | ||
['F'] = {temperature, 'C'}, | ['mi'] = {'length', 1609.344, 0, 'km'}, | ||
['°K'] = {temperature, '°C'}, | ['ft'] = {'length', 0.3048, 0, 'm'}, | ||
['°C'] = {temperature, '°F'}, | ['K'] = {'temperature', 1, 273.15, 'C'}, | ||
['°F'] = {temperature, '°C'}, | ['C'] = {'temperature', 1, 0, 'F'}, | ||
['F'] = {'temperature', 1.8, 32, 'C'}, | |||
['°K'] = {'temperature', 1, 273.15, '°C'}, | |||
['°C'] = {'temperature', 1, 0, '°F'}, | |||
['°F'] = {'temperature', 1.8, 32, '°C'}, | |||
} | } | ||
-------END DATA TABLE----- | -------END DATA TABLE----- | ||
Line 149: | Line 117: | ||
end | end | ||
local function cvtround(invalue, parms, in_unit_table, out_unit_table) | |||
-- Convert given invalue using parms, in_unit_table, out_unit_table (return '' if invalue == nil). | |||
local function cvtround(invalue, parms, | |||
-- Convert given invalue using parms, | |||
-- Return rounded, formatted string for result, using rounding | -- Return rounded, formatted string for result, using rounding | ||
-- specified in parms. | -- specified in parms. | ||
Line 174: | Line 126: | ||
local text = '' | local text = '' | ||
if invalue == nil then return text end | if invalue == nil then return text end | ||
local outvalue = | local outvalue = scaled(invalue, in_unit_table, out_unit_table) | ||
local round_to = parms.round_to | local round_to = parms.round_to | ||
local sigfig = parms.sigfig | local sigfig = parms.sigfig | ||
Line 231: | Line 183: | ||
local function process(parms) | local function process(parms) | ||
local | -- If we can convert from given in to out unit, return the table values for the two given unit types. | ||
local outext = cvtround(parms.value, parms, | local in_unit_table, outdefault = units:lookup(parms.in_unit) | ||
local outext2 = cvtround(parms.value2, parms, | if parms.out_unit == nil then -- need to catch empty string also? | ||
parms.out_unit = outdefault | |||
end | |||
local out_unit_table = units:lookup(parms.out_unit) | |||
if t1[units.utype] ~= t2[units.utype] then | |||
local msg = 'Cannot convert %s to %s' | |||
error(msg:format(t1[units.utype], t2[units.utype])) | |||
end | |||
local outext = cvtround(parms.value, parms, in_unit_table, out_unit_table) | |||
local outext2 = cvtround(parms.value2, parms, in_unit_table, out_unit_table) | |||
local intext = parms.in_text | local intext = parms.in_text | ||
local intext2 = parms.in_text2 | local intext2 = parms.in_text2 |
Revision as of 22:00, 5 September 2012
Documentation for this module may be created at Module:Convert/doc
--[[
Start of convert that I hope will be driven from tables.
Lots to do (first want to fix the default rounding).
I (enwiki Johnuniq) started this two days ago; it's not ready for release
but I'm dropping it here per BRD to see if others want to work with this.
Testing makes me think these results will occur with current code:
{{convert|3.21|kg|lb|2}} 3.21 kg (7.08 lb)
{{convert|212|F|C|2}} 212 F (100.00 C)
{{convert|5|to|10|kg|lb|2}} 5 to 10 kg (11.02 to 22.05 lb)
{{convert|5|-|10|kg|lb|2}} 5–10 kg (11.02–22.05 lb)
{{convert|15|ft|m|2}} 15 ft (4.57 m)
{{convert|3.21|kg|m|2}} Error: Cannot convert mass to length
]]--
--[[-----BEGIN DATA TABLE-----
Plan to write a program to generate the conversion tables below.
The input would be a text file in human-friendly format, and
the output would be the tables following like length/mass/units.
Values from http://en.wikipedia.org/wiki/Conversion_of_units
Check with http://en.wikipedia.org/wiki/Template:Convert/list_of_units
]]--
local function scaled(value, in_unit, out_unit)
-- Return scaled value for a simple convert.
return (value - in_unit[unit.offset]) / in_unit[unit.scale] * out_unit[unit.scale] + out_unit[unit.offset]
end
local units = {
-- Each value is {converter_table, default_out_unit}.
lookup = function (self, unit)
-- If unit is known, return its converter_table, default_out_unit.
local t = self[unit]
if t == nil then
local msg = 'Unit %s not known[[Category:Convert unknown unit]]'
error(msg:format(unit))
end
return t
end,
utype = 1,
scale = 2,
offset = 3,
defaultunit = 4,
['kg'] = {'mass', 1, 0, 'lb'},
['lb'] = {'mass', 0.45359237, 0, 'kg'},
['m'] = {'length', 1, 0, 'ft'},
['km'] = {'length', 1000, 0, 'mi'},
['mi'] = {'length', 1609.344, 0, 'km'},
['ft'] = {'length', 0.3048, 0, 'm'},
['K'] = {'temperature', 1, 273.15, 'C'},
['C'] = {'temperature', 1, 0, 'F'},
['F'] = {'temperature', 1.8, 32, 'C'},
['°K'] = {'temperature', 1, 273.15, '°C'},
['°C'] = {'temperature', 1, 0, '°F'},
['°F'] = {'temperature', 1.8, 32, '°C'},
}
-------END DATA TABLE-----
local function require_number(value, missing, invalid)
-- If value is missing or not a number, throw an error.
-- Return value as a number if valid.
if value == nil then error(missing) end
local number = tonumber(value)
if number == nil then error(invalid:format(value)) end
return number
end
local function require_integer(value, missing, invalid)
-- If value is missing or not an integer, throw an error.
-- Return value as a number if valid.
local number = require_number(value, missing, invalid)
if number ~= math.floor(number) then
error(invalid:format(value))
end
return number
end
local function get_parms(pframe)
-- Return table with all arguments passed by template converted to
-- named arguments. The numeric args are used to add named args:
-- in_text, in_text2 (strings given for value, value2)
-- value, in_unit, out_unit, value2, range, round_to
-- (except for range, which is nil or a table, the named args that are
-- added here could be provided by the user of the template).
local range_types = { -- text to separate input, output ranges
['and'] = {' and ', ' and '},
['by'] = {' by ', ' by '},
['to'] = {' to ', ' to '},
['-'] = {'–', '–'},
['to(-)'] = {' to ', '–'},
['x'] = {' by ', ' × '},
['+/-'] = {' ± ', ' ± '},
}
local args = {} -- arguments passed to template
for k,v in pframe:argumentPairs() do
args[k] = v
end
args.in_text = args[1]
args.value = require_number(args.in_text, 'Need value', 'Value "%s" must be a number')
local in_unit = args[2]
local i = 3
local range = range_types[in_unit]
if range ~= nil then
args.in_text2 = args[3]
args.value2 = require_number(args.in_text2, 'Need second value', 'Second value "%s" must be a number')
in_unit = args[4]
i = 5
end
local out_unit = args[i]
local round_to = args[i+1]
if in_unit == nil then error('Need input unit') end
args.in_unit = in_unit
args.out_unit = out_unit
args.range = range
args.round_to = round_to
return args
end
local function cvtround(invalue, parms, in_unit_table, out_unit_table)
-- Convert given invalue using parms, in_unit_table, out_unit_table (return '' if invalue == nil).
-- Return rounded, formatted string for result, using rounding
-- specified in parms.
-- This code combines convert/round because some rounding requires
-- knowledge of what we are converting.
-- TODO Fix!
local text = ''
if invalue == nil then return text end
local outvalue = scaled(invalue, in_unit_table, out_unit_table)
local round_to = parms.round_to
local sigfig = parms.sigfig
local disp = parms.disp
if round_to then
-- Ignore sigfig, disp.
round_to = require_integer(round_to, 'Need value', 'round_to "%s" must be an integer')
if round_to >= 0 then
local fmt = '%.' .. string.format('%.0f', round_to) .. 'f'
text = string.format(fmt, outvalue)
else
factor = 10^(-round_to)
zeroes = string.sub(tonumber(factor), 2)
text = string.format('%.0f', outvalue/factor) .. zeroes
end
elseif sigfig then
-- Ignore disp.
sigfig = require_integer(sigfig, 'Need value', 'sigfig "%s" must be an integer')
if sigfig <= 0 then
msg = 'sigfig "%s" must be positive'
error(msg:format(parms.sigfig))
end
local fmt = '%.' .. string.format('%.0f', sigfig) .. 'g'
text = string.format(fmt, outvalue)
elseif disp == '5' then
local negative = false
if outvalue < 0 then
negative = true
outvalue = -outvalue
end
outvalue = math.floor((outvalue / 5) + 0.5) * 5
if negative then
outvalue = -outvalue
end
text = string.format('%.0f', outvalue)
else
-- Default rounding.
text = outvalue .. 'TODO: default rounding'
end
return text
end
local disp_single = {
['or'] = '%s %s or %s %s',
['sqbr'] = '%s %s [%s %s]',
['comma'] = '%s %s, %s %s',
['b'] = '%s %s (%s %s)',
}
local disp_double = {
['or'] = '%s%s%s %s or %s%s%s %s',
['sqbr'] = '%s%s%s %s [%s%s%s %s]',
['comma'] = '%s%s%s %s, %s%s%s %s',
['b'] = '%s%s%s %s (%s%s%s %s)',
}
local function process(parms)
-- If we can convert from given in to out unit, return the table values for the two given unit types.
local in_unit_table, outdefault = units:lookup(parms.in_unit)
if parms.out_unit == nil then -- need to catch empty string also?
parms.out_unit = outdefault
end
local out_unit_table = units:lookup(parms.out_unit)
if t1[units.utype] ~= t2[units.utype] then
local msg = 'Cannot convert %s to %s'
error(msg:format(t1[units.utype], t2[units.utype]))
end
local outext = cvtround(parms.value, parms, in_unit_table, out_unit_table)
local outext2 = cvtround(parms.value2, parms, in_unit_table, out_unit_table)
local intext = parms.in_text
local intext2 = parms.in_text2
local range = parms.range
local disp = parms.disp
local wikitext
if range == nil then
wikitext = disp_single[disp] or disp_single['b']
wikitext = wikitext:format(intext, parms.in_unit, outext, parms.out_unit)
else
wikitext = disp_double[disp] or disp_double['b']
wikitext = wikitext:format(intext, range[1], intext2, parms.in_unit, outext, range[2], outext2, parms.out_unit)
end
return wikitext
end
-- Used by template {{convert2}}.
-- We will have to keep old {{convert}} for a long time, and run
-- {{convert2}} in parallel with {{convert}} while testing/developing.
local p = {}
local bodge = require "Module:mw" -- This fixes up mw.text.tag for us.
function p.convert(frame)
local pframe = frame:getParent()
local parms = get_parms(pframe)
local state,text = pcall(process, parms)
if not state then
local params = {style="color:black; background-color:orange;"}
text=mw.text.tag({name="span", contents="[[Module talk:Convert|Conversion error]]: " .. text, params=params})
end
return text
end
return p