Module:Expr
--[=[ 2016-05-26
Expr
* max
* min
* TemplateMax
* TemplateMin
* booland
]=]
local messagePrefix = "lua-module-Expr-"
local l10nDef = {}
l10nDef[ "en" ] = {
ErrorExpr = "Error in mathematical expression, function#parameter"
}
l10nDef[ "de" ] = {
ErrorExpr = "Fehler in mathematischem Ausdruck, Funktion#Parameter"
}
local function factory( say )
-- Retrieve localized message string in content language
-- Precondition:
-- say -- string; message ID
-- Postcondition:
-- Return some message string
-- Uses:
-- > messagePrefix
-- > l10nDef
-- mw.language.getContentLanguage()
-- mw.message.new()
local c = mw.language.getContentLanguage():getCode()
local m = mw.message.new( messagePrefix .. say )
local r = false
if m:isBlank() then
local l10n = l10nDef[ c ]
if not l10n then
l10n = l10nDef[ "en" ]
end
r = l10n[ say ]
else
m:inLanguage( c )
r = m:plain()
end
if not r then
r = "(((" .. say .. ")))"
end
return r
end -- factory()
local function eval( source, frame )
-- Evaluate expression
-- Precondition:
-- source -- string; mathematical expression
-- frame -- object
return frame:callParserFunction( "#expr", source )
end -- eval()
local function expr( source, frame, show )
-- Safe evaluation of presumable expression
-- Precondition:
-- source -- string; mathematical expression
-- frame -- object
-- show -- string; details about source
-- Postcondition:
-- throws error, if expression failed
-- returns number with resulting figure
-- Uses:
-- factory()
local lucky, r = pcall( eval, source, frame )
local n = tonumber( r, 10 )
if not lucky or n == nil then
r = r .. " " .. factory( "ErrorExpr" )
.. " ''" .. show .. "'' (" .. source .. ")"
error( r, 0 )
else
r = n
end
return r
end -- expr()
local function base62( value )
-- Convert number from and to base62 encoding
-- Precondition:
-- value -- number or string to be converted
-- number: to base62
-- string: base62 to number
-- Lua limitation at 10^53; larger numbers are less precise
-- Postcondition:
-- returns string, or number, or false
local r = false
local state = type( value )
if state == "number" then
local k = math.floor( value )
if k == value and value > 0 then
local m
r = ""
while k > 0 do
m = k % 62
k = ( k - m ) / 62
if m >= 36 then
m = m + 61
elseif m >= 11 then
m = m + 55
else
m = m + 48
end
r = string.char( m ) .. r
end
elseif value == 0 then
r = "0"
end
elseif state == "string" then
if value:match( "^%w+$" ) then
local n = #value
local k = 1
local c
r = 0
for i = n, 1, -1 do
c = value:byte( i, i )
if c >= 48 and c <= 57 then
c = c - 48
elseif c >= 65 and c <= 90 then
c = c - 55
elseif c >= 97 and c <= 122 then
c = c - 61
else -- How comes?
r = nil
break -- for i
end
r = r + c * k
k = k * 62
end -- for i
end
end
return r
end -- base62()
function logicaland(args)
local r = true;
local k, v, s
local b
for k, v in pairs(args) do
s = mw.text.trim(v)
b = (s or '') ~= ''
r = r and b
end
return r
end
function logicalor(args)
local r = false;
local k, v, s
local b
for k, v in pairs(args) do
s = mw.text.trim(v) or '';
if s == '' then
b = false;
elseif s=='0' then
b = false;
elseif s=='false' then
b = false;
elseif s=='falsch' then
b = false;
else
b = true;
end
if b then
r = true;
end
end
return r
end
local function minmax( params, frame, low, lazy )
-- Find extremum of unnamed params values
-- Precondition:
-- params -- table; like args
-- .minus
-- .zeroBlank
-- frame -- object
-- low -- true: minimum; false: maximum
-- lazy -- true: try numeric result; false: return string
-- Postcondition:
-- throws error, if expression failed
-- returns number, or
-- string if formatting required, or
-- false if no data provided
-- Uses:
-- expr()
local k, v, n, scope
local light = ( params.minus ~= "-" )
local luxury = ( params.minus and light )
local c = mw.ustring.char( 8722 ) -- minus
local scan = "^%s*%-?[0-9]*%.?[0-9]*%s*$"
local r = false
for k, v in pairs( params ) do
if type( k ) == "number" then
scope = type( v )
if scope == "string" then
if v:match( "^%s*$" ) then
n = false
else
if mw.ustring.match( v, c ) then
luxury = light
v = mw.ustring.gsub( v, c, "-" )
end
if not mw.ustring.match( v, scan ) then
if low then
scope = "min()#"
else
scope = "max()#"
end
scope = scope .. tostring( k )
v = expr( v, frame, scope )
end
n = tonumber( v )
end
elseif scope == "number" then
n = v
else
n = false
end
if n then
if r then
if low then
if n < r then
r = n
end
else
if n > r then
r = n
end
end
else
r = n
end
end
end
end -- for k, v
if r then
if luxury and r < 0 then
r = c .. tostring( -1 * r )
elseif not lazy then
if r == 0 then
if params.zeroBlank then
r = ""
else
r = "0"
end
else
r = tostring( r )
end
end
end
return r
end -- minmax()
-- Export
local p = {}
function p.base62( frame )
local r
local s = frame.args[ 1 ]
if s then
local s2 = frame.args[ 2 ]
if s2 then
s2 = mw.text.trim( s2 )
end
if s2 == "D2B" then
s = tonumber( s )
else
s = mw.text.trim( s )
s2 = false
end
r = base62( s )
if r and not s2 then
r = string.format( "%17d", r )
end
end
return r or ""
end
function p.max( frame )
local lucky, r = pcall( minmax, frame.args, frame, false, false )
return r or ""
end
function p.min( frame )
local lucky, r = pcall( minmax, frame.args, frame, true, false )
return r or ""
end
function p.TemplateMax( frame )
return p.max( frame:getParent() )
end
function p.TemplateMin( frame )
return p.min( frame:getParent() )
end
function p.booland(frame)
local fr=frame:getParent()
return logicaland(fr.args)
end
function p.boolor(frame)
local fr=frame:getParent()
return logicalor(fr.args)
end
function p.Expr( f, a )
local r = false
if f == "min" or f == "max" then
local frame = mw.getCurrentFrame()
local low = ( f == "min" )
local lucky
lucky, r = pcall( minmax, a, frame, low, true )
elseif f == "base62" then
r = base62( a )
end
return r
end -- .Expr()
return p -- Expr