Vorlagenprogrammierung Diskussionen Lua Test Unterseiten
Modul Deutsch English


local Str = { suite  = "Str",
              serial = "2018-09-03",
              item   = 0 }
--[=[
{{Template:Str *********}}
]=]



function fixPattern( arg )
    -- Diasable Lua pattern syntax.
    -- Precondition:
    --     string; to be escaped
    -- Postcondition:
    --     Returns string with escaped syntax elements.
	return mw.ustring.gsub( arg, ".", { ["%"]  = "%%";
	                                    ["^"]  = "%^";
	                                    ["$"]  = "%$";
	                                    ["."]  = "%.";
	                                    ["("]  = "%(";
	                                    [")"]  = "%)";
	                                    ["["]  = "%[";
	                                    ["]"]  = "%]";
	                                    ["?"]  = "%?";
	                                    ["*"]  = "%*";
	                                    ["+"]  = "%+";
	                                    ["-"]  = "%-";
	                                    ["\0"] = "%z"; } )
end -- fixPattern()



function Str.crop( args )
    -- Remove the right-most {2} characters of a string {1}.
    -- Precondition:
    --     1 = A string
    --     2 = A length; 1 if omitted
    -- Postcondition:
    --     Throws error if {2} < 0
    --     Returns string; remaining starting characters, if any.
    local r
    if args[ 1 ] then
        local n = tonumber( args[ 2 ] )
        r = mw.ustring.len( args[ 1 ] )
        if not n then
            if args[ 2 ] then
                n = r
            else
                n = 1
            end
        elseif n < 0 then
            error( "&#123;{Str&nbsp;crop&#124;&#124;"
                   .. args[ 2 ] .. "&lt;0}}",  0 )
        end
        if n >= r then
            r = ""
        else
            if n == 0 then
                r = args[ 1 ]
            elseif n == r then
                r = ""
            else
                r = mw.ustring.sub( args[ 1 ],  1,  r - n )
            end
        end
    else
        r = ""
    end
    return r
end -- .crop()



function Str.cropleft( args )
    -- Remove the left-most {2} characters of a string {1}.
    -- Precondition:
    --     1 = A string
    --     2 = A length; 1 if omitted
    -- Postcondition:
    --     Throws error if {2} < 0
    --     Returns string; remaining starting characters, if any.
    local r
    if args[ 1 ] then
        local n = tonumber( args[ 2 ] )
        r = mw.ustring.len( args[ 1 ] )
        if not n then
            if args[ 2 ] then
                n = r
            else
                n = 1
            end
        elseif n < 0 then
            error( "&#123;{Str&nbsp;cropleft&#124;&#124;"
                   .. args[ 2 ] .. "&lt;0}}",  0 )
        end
        if n >= r then
            r = ""
        else
            n = math.floor( n )
            if n == 0 then
                r = args[ 1 ]
            elseif n == r then
                r = ""
            else
                r = mw.ustring.sub( args[ 1 ],  n + 1 )
            end
        end
    end
    return r or ""
end -- .cropleft()



function Str.find( args )
    -- Position of first appearance of {2} in {1}.
    -- Precondition:
    --     1 = A base_string
    --     2 = A sub_string
    -- Postcondition:
    --     Returns string; -1 if sub_string not found
    --                     Character position is 1 based
    --                     (not 0 based as usual in calculations).
    local r
    if args[ 1 ] and args[ 2 ] then
        local k
        r, k = mw.ustring.find( args[ 1 ], args[ 2 ], 1, true )
        if not r then
            r = -1
        end
    elseif args[ 1 ] then
        r = 1
    else
        r = -1
    end
    return r
end -- .find()



function Str.ge_len( args )
    -- Conditional answer depending on string {1} length wrt {2}.
    -- Template:Str ≥ len
    -- Precondition:
    --     1 = A string
    --     2 = A length
    --     3 = Data to return/render when "longer than or equally long".
    --     4 = Data to return/render when "shorter than".
    -- Postcondition:
    --     Throws error if {1} undefined or invalid.
    --     Returns string; either {3} or {4}.
    local r
    if args[ 1 ] then
        local n = tonumber( args[ 2 ] )
        r = mw.ustring.len( args[ 1 ] )
        if not n then
            r = args[ 3 ] or ""
        elseif r < n then
            r = args[ 4 ] or ""
        else
            r = args[ 3 ] or ""
        end
    else
        error( "&#123;{Str&nbsp;&#8805;&nbsp;len}}", 0 )
    end
    return r
end -- .ge_len()



function Str.hex2dez( args )
    -- Returns the decimal value of an octethexcode {1}.
    -- Precondition:
    --     1 = A hexcode (octets)
    return tonumber( args[ 1 ], 16 ) or 0
end -- .hex2dez()



function Str.index( args )
    -- Returns the {2}-th character of trimmed text {1}.
    -- Precondition:
    --     1 = A string
    --     2 = A position, counted from 1 in string
    -- Postcondition:
    --     Throws error if {2} < 0 or {2} out of {1}
    local r = tonumber( args[ 2 ] )
    if r then
        if r <= 0 then
            error( "&#123;{Str&nbsp;index&#124;&#124;"
                   .. args[ 2 ] .. "&lt;=0}}",  0 )
        elseif args[ 1 ] then
            local n = mw.ustring.len( args[ 1 ] )
            if n < r then
                error( "&#123;{Str&nbsp;index&#124;&#124;"
                       .. args[ 2 ] .. "&gt;&gt;}}",  0 )
            else
                r = mw.ustring.sub( args[ 1 ],  r,  r )
            end
        else
            error( "&#123;{Str&nbsp;index}}", 0 )
        end
    else
        if args[ 2 ] then
            r = args[ 2 ] .. "}}"
        else
            r = "}}"
        end
        error( "&#123;{Str&nbsp;index&#124;&#124;" .. r, 0 )
    end
    return r
end -- .index()



function Str.left( args )
    -- Gives the {2}-length substring of characters
    -- from the start of the trimmed string {1}.
    -- Precondition:
    --     1 = A string
    --     2 = A length; if omitted, 1 is used
    -- Postcondition:
    --     Return the left {2} characters of {1}.
    --     If {2} is invalid, empty or zero, an empty string is returned.
    local n, r
    if not args[ 2 ] then
        n = 1
    elseif args[ 2 ] == "" then
        n = 0
    else
        n = tonumber( args[ 2 ] )
        if not n then
            n = 0
        end
    end
    if n > 0  and  args[ 1 ] then
        r = mw.ustring.sub( args[ 1 ], 1, n )
    else
        r = ""
    end
    return r
end -- .left()



function Str.len( args )
    -- Returns length of string (excluding spaces at the start and end).
    -- Precondition:
    --     1 = A string
    local r
    if args[ 1 ] then
        r = tostring( mw.ustring.len( args[ 1 ] ) )
    else
        r = "0"
    end
    return r
end -- .len()



function Str.match( args )
    -- Get string {1} containing pattern {2}, optional in capture (3).
    -- Precondition:
    --     1 = A string; whitespace around will be kept
    --     2 = A pattern; all (1) if invalid
    --     3 = An optional number; 0 if invalid, else sequenced capture
    -- Postcondition:
    --     Returns string.
    local r = args[ 1 ]
    local s = args[ 2 ]
    if r and s then
        local k = tonumber( args[ 3 ] )
        if not k or k < 0 then
            k = 0
        else
            k = math.floor( k )
        end
        r = ( { mw.ustring.match( r, s ) } )
        if k > 0 then
            r = r[ k ]
        else
            r = table.concat( r )
        end
    end
    return r or ""
end -- .match()



function Str.repeating( args )
    -- Repeat string {1} as often as {2} indicates.
    -- Precondition:
    --     1 = A string; whitespace around will be kept
    --     2 = A factor; 1 if omitted or invalid
    -- Postcondition:
    --     Returns string.
    local r = args[ 1 ]
    if r then
        local n = tonumber( args[ 2 ] )
        if not n then
            n = 1
        end
        if n > 1 then
            r = r:rep( n )
        end
    else
        r = ""
    end
    return r
end -- .repeating()



function Str.replace( args )
    -- Replace in string {1} string {2} with string (3).
    -- Precondition:
    --     1 = A string; whitespace around will be kept
    --     2 = A seek string; whitespace around will be kept
    --     3 = A replacing string; whitespace around will be kept
    --     4 = An optional number; limit number of replacements
    --     5 = An optional boolean; 0 or 1, regard seek as pattern
    -- Postcondition:
    --     Returns string.
    local r    = args[ 1 ]
    local seek = args[ 2 ]
    if r and seek then
        local s = args[ 3 ] or ""
        local n = tonumber( args[ 4 ] )
        local l = args[ 5 ]
        if not n or n < 1 then
            n = nil
        else
            n = math.floor( n )
        end
        if l then
            l = mw.text.trim( l )
            if l ~= ""  and  l ~= "0" then
		          seek = fixPattern( seek )
		          s    = mw.ustring.gsub( s, "%%", "%%%%")
            end
        end
        r, l = mw.ustring.gsub( r, seek, s, n )
    end
    return r or ""
end -- .replace()



function Str.right( args )
    -- Gives the characters from {2} to the end of the string {1}.
    -- Precondition:
    --     1 = A string
    --     2 = An offset
    --         A negative offset is treated the same as zero,
    --         which simply returns the original string.
    local r = args[ 1 ]
    if r then
        local n = tonumber( args[ 2 ] )
        if n then
            n = math.floor( n )
        else
            n = 0
        end
        if n > 0 then
            r = mw.ustring.sub( r,  n + 1 )
        end
    end
    return r or ""
end -- .right()



function Str.rightc( args )
    -- Gives the rightmost {1} characters from {2}.
    -- Precondition:
    --     1 = A string
    --     2 = A count
    --         A negative count is treated the same as zero,
    --         which simply returns the original string.
    local r
    if r then
        local n = tonumber( args[ 2 ] )
        if n then
            n = math.floor( n )
        else
            n = 0
        end
        if n > 0 then
            r = mw.ustring.sub( r, - n )
        end
    end
    return r or ""
end -- .rightc()



function Str.split( args )
    -- Substring of string {2} split {1} time or at pattern {3}.
    -- Precondition:
    --     1 = A count (<= 0: count, else number of)
    --     2 = A string
    --     3 = A pattern
    --     4 = An optional boolean; 0 or 1, take pattern literally
    local r = args[ 2 ]
    if r then
        local n = tonumber( args[ 2 ] )
        local s = args[ 3 ] or ""
        local l = args[ 4 ]
        local e
        if n then
            n = math.floor( n )
        else
            n = 0
        end
        if l then
            l = ( l ~= "0" )
        end
        e = mw.text.split( r, s, l )
	     if n <= 0 then
	     	   r = #e
	     elseif n <= #r then
            r = e[ n ]
	     end
    end
    return r or ""
end -- .split()



function Str.sub0( args )
    -- Substring of string {1} starting at {2} and containing {3} chars.
    -- en:Template:Str sub old
    -- Precondition:
    --     1 = A string
    --     2 = An offset
    --         Base 0: the first character is numbered 0, and so on.
    --     3 = A length
    local r
    local e = false
    if args[ 1 ] then
        local n = tonumber( args[ 3 ] )
        if not n then
            if args[ 3 ] then
                e = "&#124;&#124;&#124;" .. args[ 3 ] .. "}}"
            else
                n = 0
            end
        end
        if not e then
            if n < 0 then
                e = "&#124;&#124;&#124;" .. args[ 3 ] .. "&lt;0}}"
            else
                local i = tonumber( args[ 2 ] )
                if not i  or  i < 0 then
                    if args[ 2 ] then
                        e = "&#124;&#124;" .. args[ 2 ] .. "}}"
                    else
                        e = "&#124;&#124;}}"
                    end
                elseif n == 0 then
                    r = ""
                else
                    r = mw.ustring.sub( args[ 1 ],  i + 1,  i + n )
                end
            end
        end
    else
       e = "}}"
    end
    if e then
        error( "&#123;{Str&nbsp;sub0" .. e,  0 )
    end
    return r
end -- .sub0()



function Str.sub1( args )
    -- Substring of string {1} starting at {2} and containing {3} chars.
    -- en:Template:Str sub new
    -- Precondition:
    --     1 = A string
    --     2 = An offset
    --         Base 1: the first character is numbered 1, and so on.
    --     3 = A length
    local r
    local e = false
    if args[ 1 ] then
        local n = tonumber( args[ 3 ] )
        if not n then
            if args[ 3 ] then
                e = "&#124;&#124;&#124;" .. args[ 3 ] .. "}}"
            else
                n = 0
            end
        end
        if not e then
            if n < 0 then
                e = "&#124;&#124;&#124;" .. args[ 3 ] .. "&lt;0}}"
            else
                local i = tonumber( args[ 2 ] )
                if not i  or  i < 1 then
                    if args[ 2 ] then
                        e = "&#124;&#124;" .. args[ 2 ] .. "}}"
                    else
                        e = "&#124;&#124;}}"
                    end
                elseif n == 0 then
                    r = ""
                else
                    r = mw.ustring.sub( args[ 1 ],  i,  i + n )
                end
            end
        end
    else
       e = "}}"
    end
    if e then
        error( "&#123;{Str&nbsp;sub1" .. e,  0 )
    end
    return r
end -- .sub1()



function Str.failsafe( assert )
    -- Retrieve versioning and check for compliance
    -- Precondition:
    --     assert  -- string, with required version or false
    -- Postcondition:
    --     Returns  string with appropriate version, or false
    local since = assert
    local r
    if since == "wikidata" then
        local item = Str.item
        since = false
        if type( item ) == "number"  and  item > 0 then
            local ent = mw.wikibase.getEntity( string.format( "Q%d",
                                                              item ) )
            if type( ent ) == "table" then
                local vsn = ent:formatPropertyValues( "P348" )
                if type( vsn ) == "table"  and
                   type( vsn.value ) == "string"  and
                   vsn.value ~= "" then
                    r = vsn.value
                end
            end
        end
    end
    if not r then
        if not since  or  since <= Str.serial then
            r = Str.serial
        else
            r = false
        end
    end
    return r
end -- Str.failsafe()



local function Facing( frame, action, leave )
    -- Run actual code from template transclusion ot direct invocation
    -- Precondition:
    --     frame   -- object
    --     action  -- string with function name
    --     leave   -- true: keep whitespace around
    -- Postcondition:
    --     Return string; like the legacy templates
    --                    might be error message
    local got = frame.args
    local lucky, r
    if not pairs( got ) then
        -- no #invoke parameters, try transclusion parameters
        got = frame:getParent().args
    end -- for k, v
    if not leave then
        for k, v in pairs( got ) do
            got[ k ] = mw.text.trim( v )
        end -- for k, v
    end
    lucky, r = pcall( Str[ action ], got )
    if not lucky then
        local e = mw.html.create( "span" )
        r = tostring( e:addClass( "error" )
                       :wikitext( r ) )
    end
    return r
end -- Facing()



-- Export
local p = { }

function p.TEST( action, args )
    -- Run main code from test environment
    -- Precondition:
    --     action  -- string with function name
    --     args    -- table; simulated environment
    -- Postcondition:
    --     Return string; like the legacy templates
    local lucky, r
    for k, v in ipairs( args ) do
        args[ k ] = mw.text.trim( v )
    end -- for k, v
    lucky, r = pcall( Str[ action ], args )
    return r
end -- p.TEST()

p.crop = function ( frame )
    return Facing( frame, "crop" )
end
p.cropleft = function ( frame )
    return Facing( frame, "cropleft" )
end
p.find = function ( frame )
    return Facing( frame, "find" )
end
p.hex2dez = function ( frame )
    return Facing( frame, "hex2dez" )
end
p.index = function ( frame )
    return Facing( frame, "index" )
end
p.left = function ( frame )
    return Facing( frame, "left" )
end
p.len = function ( frame )
    return Facing( frame, "len" )
end
p[ "≥ len" ] = function ( frame )
    return Facing( frame, "ge_len" )
end
p.match = function ( frame )
    return Facing( frame, "match", true )
end
p.repeating = function ( frame )
    return Facing( frame, "repeating", true )
end
p.replace = function ( frame )
    return Facing( frame, "replace", true )
end
p.right = function ( frame )
    return Facing( frame, "right" )
end
p.rightc = function ( frame )
    return Facing( frame, "rightc" )
end
p.split = function ( frame )
    return Facing( frame, "split" )
end
p.sub = function ( frame )
    return Facing( frame, "sub0" )
end
p.sub0 = function ( frame )
    return Facing( frame, "sub0" )
end
p.sub_old = function ( frame )
    return Facing( frame, "sub0" )
end
p.sub1 = function ( frame )
    return Facing( frame, "sub1" )
end
p.sub_new = function ( frame )
    return Facing( frame, "sub1" )
end

p.failsafe = function ( frame )
    local s = type( frame )
    local since
    if s == "table" then
        since = frame.args[ 1 ]
    elseif s == "string" then
        since = frame
    end
    if since then
        since = mw.text.trim( since )
        if since == "" then
            since = false
        end
    end
    return Str.failsafe( since ) or ""
end

p.Str = function ()
    return Str
end

return p