Modul:Expr

Aus FreeWiki
Version vom 2. Februar 2020, 20:18 Uhr von Jörg Wichmann (Admin) (Diskussion | Beiträge) (1 Version importiert)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

--[=[ 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.ustring.lower(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; elseif s=='nein' 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