Zum Inhalt springen

Modul:CoordParse

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 11. September 2022 um 16:55 Uhr durch Crazy1880 (Diskussion | Beiträge) (Schützte „Modul:CoordParse“: Häufig eingebundenes Modul: siehe Anfrage dazu ([Bearbeiten=Nur Sichter] (unbeschränkt) [Verschieben=Nur Administratoren] (unbeschränkt))). Sie kann sich erheblich von der aktuellen Version unterscheiden.
Vorlagenprogrammierung Diskussionen Lua Unterseiten
Modul Deutsch English

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus

Dies ist die (produktive) Mutterversion eines global benutzten Lua-Moduls.
Wenn die serial-Information nicht übereinstimmt, müsste eine Kopie hiervon in das lokale Wiki geschrieben werden.
Versionsbezeichnung auf WikiData: 2022-09-15

Updating notwendig

(lokal: 2022-09-07)

local CoordParse = { suite  = "CoordParse",
                     serial = "2022-09-07",
                     item   = 0 }
--[==[
Coordinate parsing and validation
* feed
* fragments
* failsafe
]==]
local Failsafe = CoordParse



local function factory()
    -- Create patterns
    -- Postcondition:
    --     Patterns available
    if not CoordParse.re then
        CoordParse.re = {
            Wspace = mw.ustring.char( 91, 37, 115,
                                          0xA0,
                                          0x1680,
                                          0x2000, 45, 0x200A,
                                          0x202F,
                                          0x205F,
                                          0x3000,
                                          0x303F, 93 ),
            Deg    = mw.ustring.char( 91, 0xB0,
                                          0xBA, 93 ),
            Min    = mw.ustring.char( 91, 0x27,
                                          0x2032,
                                          0x2019, 93 ),
            Sec    = mw.ustring.char( 91, 0x22,
                                          0x2033,
                                          0x201D, 93 )
        }
    end
end -- factory()



local function fair( adjust )
    -- Advanced trim
    -- Precondition:
    --     adjust  -- string, to be trimmed, or something else
    --     CoordParse.re has been initialized
    -- Postcondition:
    --     Return trimmed string, or false if empty
    local r
    if type( adjust ) == "string" then
        local s = string.format( "^%s+", CoordParse.re.Wspace )
        r = mw.ustring.gsub( adjust, s, "" )
        if r == "" then
            r = false
        else
            s = string.format( "%s+$", CoordParse.re.Wspace )
            r = mw.ustring.gsub( adjust, s, "" )
        end
    else
        r = adjust
    end
    return r
end -- fair()



local function fault( apply, about )
    -- Error message
    -- Precondition:
    --     apply  -- string, with message ID, or not
    --     about  -- string, with details, or not
    -- Postcondition:
    --     Return mw.html object
    local r = mw.html.create( "span" )
    local s
    if apply then
        if CoordParse.err then
            local std = CoordParse.err[ "err" .. apply ]
            if type( std ) == "string" then
                std = mw.text.trim( std )
                if std ~= "" then
                    s = std
                end
            end
        end
        if not s then
            s = string.format( "((%s))", apply )
        end
    end
    if about then
        if s then
            s = string.format( "%s: %s",  s,  mw.text.nowiki( about ) )
        else
            s = about
        end
    end
    if CoordParse.err  and
        type( CoordParse.err.Cat ) == "string"   and
        CoordParse.err.Cat ~= "" then
        s = string.format( "%s[[Category:%s]]",
                           s,
                           CoordParse.err.Cat )
    end
    r:addClass( "error" )
     :wikitext( s )
    return r
end -- fault()



local function fetch()
    -- Retrieve Expr library
    -- Postcondition:
    --     Return some message string, if failed
    local r
    if CoordParse.Expr then
        if type( CoordParse.Expr ) == "string" then
            r = CoordParse.Expr
        end
    else
        local lucky
        lucky, CoordParse.Expr = pcall( require, "Module:Expr" )
        if type( CoordParse.Expr ) == "table" then
            lucky, CoordParse.Expr = pcall( CoordParse.Expr )
            if type( CoordParse.Expr ) ~= "table"  or
               type( CoordParse.Expr.figure ) ~= "function"  or
               type( CoordParse.Expr.minsec2decimal ) ~= "function" then
                r = "Invalid library 'Expr'"
            end
        else
            r = CoordParse.Expr
        end
        if r then
            r               = tostring( fault( r ) )
            CoordParse.Expr = r
        end
    end
    return r
end -- fetch()



local function field( apply, align, arglist )
    -- Parse compass direction word
    -- Precondition:
    --     apply    -- string, with word, or not
    --     align    -- true, for latitude
    --     arglist  -- table, with options
    -- Postcondition:
    --     Return
    --         1   -- mw.html object, with error message, if failed
    --         2   -- string, with SNWE letter, or not
    local r1, r2
    if apply  and  mw.ustring.match( apply, "^%a+$" ) then
        local supply = mw.ustring.upper( apply )
        local scan
        if align then
            scan = string.format( "^%s$",  arglist.N or "N" )
            if mw.ustring.match( supply, scan ) then
                r2 = "N"
            else
                scan = string.format( "^%s$",  arglist.S or "S" )
                if mw.ustring.match( supply, scan ) then
                    r2 = "S"
                end
            end
        else
            scan = string.format( "^%s$",  arglist.E or "E" )
            if mw.ustring.match( supply, scan ) then
                r2 = "E"
            else
                scan = string.format( "^%s$",  arglist.W or "W" )
                if mw.ustring.match( supply, scan ) then
                    r2 = "W"
                end
            end
        end
        if not r2 then
            r1 = fault( "Word", apply )
        end
    end
    return r1, r2
end -- field()



local function figure( analyze )
    -- Parse string
    -- Precondition:
    --     analyze   - string or number, with figure
    --     Expr available
    -- Postcondition:
    --     Return number or not
    local s = type( analyze )
    local r
    if s == "string" then
        if analyze:find( "," )  then
           s = "-,"
        else
           s = "-."
        end
        r = CoordParse.Expr.figure( analyze, s )
    elseif s == "number" then
        r = analyze
    end
    return r
end -- figure()



local function fracking( apply, accept )
    -- Parse number with unit
    -- Precondition:
    --     apply   -- string, with coordinate remainder
    --     accept  -- string, with key for pattern of unit
    --                        -- "Deg"
    --                        -- "Min"
    --                        -- "Sec"
    --                        -- "Min2"
    -- Postcondition:
    --     Return
    --         1   -- number, if found
    --         2   -- string, with remainder, or not
    local s = CoordParse.re[ accept ]
    local i, j = mw.ustring.find( apply, s, 2 )
    local r1, r2
    if i then
        s = mw.ustring.sub( apply,  1,  i - 1 )
        r1 = figure( s )
        if r1 then
            r2 = fair( mw.ustring.sub( apply,  j + 1 ) )
        end
    end
    return r1, r2
end -- fracking()



local function from( apply, align, arglist )
    -- Parse string
    -- Precondition:
    --     apply    -- string, with coordinate
    --     align    -- true, for latitude
    --     arglist  -- table, with options
    -- Postcondition:
    --     Return  -- mw.html object, with error message, if failed
    --             -- number, if success
    local g, r, snwe
    if apply:find( "/", 1, true ) then
        local n
        g = mw.text.split( apply, "%s*/%s*" )
        n = #g
        if n > 4 then
            r = fault( "GT4", apply )
        else
            r, snwe = field( fair( g[ n ] ),  align,  arglist )
            if not r then
                if snwe then
                    g[ n ] = false
                    n      = n - 1
                end
            end
        end
    else
        local s = fair( apply )
        local start, suffix
        if s then
            start, suffix = mw.ustring.match( s, "^(.*%A)(%a+)$" )
            if start then
                r, snwe = field( suffix, align, arglist )
                s = fair( start )
            end
        else
            r = fault( "Empty", s )
        end
        if not r then
            r = fetch()
        end
        if not r then
            g = { }
            start, suffix = fracking( s, "Deg" )
            if start then
                table.insert( g, start )
                s = fair( suffix )
                if s then
                    start, suffix = fracking( s, "Min" )
                    if start then
                        table.insert( g, start )
                        s = fair( suffix )
                        if s then
                            start, suffix = fracking( s, "Sec" )
                            if start then
                                table.insert( g, start )
                            else
                                if not r then
                                    CoordParse.re.Min2 =
                                                     CoordParse.re.Min ..
                                                     CoordParse.re.Min
                                end
                                start, suffix = fracking( s, "Min2" )
                                if start then
                                    table.insert( g, start )
                                else
                                    r = fault( "Bad", s )
                                end
                            end
                            s = fair( suffix )
                            if s then
                                r = fault( "Bad", s )
                            end
                        end
                    else
                        r = fault( "Bad", s )
                    end
                end
            else
                r = fetch()
                if not r then
                s = figure( s )
                    if s then
                        table.insert( g, s )
                    else
                        r = fault( "Bad", s )
                    end
                end
            end
        end
    end
    if not r then
        if not snwe then
            if align then
                snwe = "N"
            else
                snwe = "E"
            end
        end
        for i = #g + 1,  3 do
            table.insert( g, false )
        end -- for i
        if g == 3 then
            table.insert( g, snwe )
        else
            g[ 4 ] = snwe
        end
        r = CoordParse.fragments( align, g, arglist )
    end
    return r
end -- from()



CoordParse.feed = function ( align, adjust, arglist )
    -- Parse single string
    -- Precondition:
    --     align    -- true, for latitude
    --     adjust   -- string, to be parsed
    --     arglist  -- table, with options
    -- Postcondition:
    --     Return single number, or mw.html error message
    local r, stuff
    if type( arglist ) == "table" then
        CoordParse.err = arglist
        if type( adjust ) == "string" then
            factory()
            stuff = adjust
            if stuff:find( "<", 1, true ) then
                stuff = stuff:gsub( "<[^>]*>", "" )
            end
            if stuff:find( "&.+;" ) then
                stuff = mw.text.decode( stuff, true )
            end
            r = from( stuff, align, arglist )
        end
    end
    if not stuff then
        r = fault( "Empty" )
    end
    return r
end -- CoordParse.feed()



CoordParse.fragments = function ( align, array, arglist )
    -- Parse component set
    -- Precondition:
    --     align    -- true, for latitude
    --     array    -- sequence table, with components
    --                 [ 1 ]  -- string or number, with degree
    --                 [ 2 ]  -- string or number, with minutes, or not
    --                 [ 3 ]  -- string or number, with seconds, or not
    --                 [ 4 ]  -- string or not, with direction
    --     arglist  -- table, with options
    -- Postcondition:
    --     Return single number, or mw.html error message
    local r
    if type( array ) == "table"  and  type( arglist ) == "table" then
        local g = { }
        local max, min, s, v
        factory()
        for i = 1, #array do
            v = array[ i ]
            s = type( v )
            if s == "string" then
                v = fair( v )
                if v  and  i < 4 then
                    max = i
                    if not min  and  v:find( "[%.,]" ) then
                        min = i
                    end
                end
            elseif s == "number" then
                if i < 4 then
                    max = i
                    if not min  and  v ~= math.floor( v ) then
                        min = i
                    end
                end
            else
                v = false
            end
            table.insert( g, v )
        end -- for i
        for i = #g + 1, 4 do
            table.insert( g, false )
        end -- for i
        if g[ 1 ] then
            if g[ 3 ]  and  not g[ 2 ] then
                r = fault( "MinX" )
            elseif min  and  min < max then
                r = fault( "SepEl",  tostring( g[ min ] ) )
            else
                r = fetch()
                if not r then
                    for i = 1, max do
                        v = g[ i ]
                        if type( v ) == "string" then
                            v = figure( v )
                            if v then
                                g[ i ] = v
                            else
                                r = fault( "Num", v )
                                break -- for i
                            end
                        end
                    end -- for i
                end
                if not r  and  max > 1 then
                    v = g[ 2 ]
                    if v < 0 then
                        r = fault( "Mlt0",  tostring( v ) )
                    elseif v >= 60 then
                        r = fault( "Mgt60",  tostring( v ) )
                    elseif max > 2 then
                        v = g[ 3 ]
                        if v < 0 then
                            r = fault( "Slt0",  tostring( v ) )
                        elseif v >= 60 then
                            r = fault( "Sgt60",  tostring( v ) )
                        end
                    end
                end
                if not r then
                    v = g[ 1 ]
                    if align then
                        if v < -90 then
                            r = fault( "DegLT",  tostring( v ) )
                        elseif v > 90 then
                            r = fault( "DegGT",  tostring( v ) )
                        end
                    else
                        if v <= -180 then
                            r = fault( "DegLT",  tostring( v ) )
                        elseif v > 180 then
                            if v < 360 then
                                g[ 1 ] = v - 360
                            else
                                r = fault( "DegGT",  tostring( v ) )
                            end
                        end
                    end
                    if not r then
                        r = CoordParse.Expr.minsec2decimal( g[ 1 ],
                                                            g[ 2 ],
                                                            g[ 3 ],
                                                            g[ 4 ] )
                        if r  and
                           g[ 4 ]  and
                           r > 0  and
                           ( g[ 4 ] == "S"  or
                             g[ 4 ] == "W" ) then
                            r = -1 * r
                        end
                    end
                end
            end
        else
            r = fault( "DegX" )
        end
    end
    return r
end -- CoordParse.fragments()



Failsafe.failsafe = function ( atleast )
    -- Retrieve versioning and check for compliance
    -- Precondition:
    --     atleast  -- string, with required version
    --                         or wikidata|item|~|@ or false
    -- Postcondition:
    --     returns  string  -- with queried version/item, also if problem
    --              false   -- if appropriate
    -- 2020-08-17
    local since  = atleast
    local last   = ( since == "~" )
    local linked = ( since == "@" )
    local link   = ( since == "item" )
    local r
    if last  or  link  or  linked  or  since == "wikidata" then
        local item = Failsafe.item
        since = false
        if type( item ) == "number"  and  item > 0 then
            local suited = string.format( "Q%d", item )
            if link then
                r = suited
            else
                local entity = mw.wikibase.getEntity( suited )
                if type( entity ) == "table" then
                    local seek = Failsafe.serialProperty or "P348"
                    local vsn  = entity:formatPropertyValues( seek )
                    if type( vsn ) == "table"  and
                       type( vsn.value ) == "string"  and
                       vsn.value ~= "" then
                        if last  and  vsn.value == Failsafe.serial then
                            r = false
                        elseif linked then
                            if mw.title.getCurrentTitle().prefixedText
                               ==  mw.wikibase.getSitelink( suited ) then
                                r = false
                            else
                                r = suited
                            end
                        else
                            r = vsn.value
                        end
                    end
                end
            end
        end
    end
    if type( r ) == "nil" then
        if not since  or  since <= Failsafe.serial then
            r = Failsafe.serial
        else
            r = false
        end
    end
    return r
end -- Failsafe.failsafe()



-- Export
local p = {}

function p.feed( frame )
    local latitude = frame.args.latitude
    if latitude == "1" then
        latitude = true
    else
        latitude = false
    end
    return tostring( CoordParse.feed( latitude,
                                      frame.args[ 1 ],
                                      frame.args ) )
end

p.failsafe = function ( frame )
    -- Versioning interface
    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 Failsafe.failsafe( since )  or  ""
end -- p.failsafe

setmetatable( p,  { __call = function ( func, ... )
                                 setmetatable( p, nil );
                                 return Failsafe;
                             end } );

return p