    {-|
    Module      : HsLua.Util
    Copyright   : © 2007–2012 Gracjan Polak;
                  © 2012–2016 Ömer Sinan Ağacan;
                  © 2017-2024 Albert Krewinkel
    License     : MIT
    Maintainer  : Albert Krewinkel <tarleb@hslua.org>
    Stability   : beta
    Portability : non-portable (depends on GHC)

    HsLua utility functions.
    -}
    module HsLua.Util
      ( getglobal'
      , setglobal'
      ) where

    import Data.List (groupBy)
    import Data.String (IsString (fromString))
    import HsLua.Core
      ( LuaE, LuaError (..), Name (..), getfield, getglobal, nth, pop
      , pushvalue, remove, setfield, setglobal, top )
    import qualified HsLua.Core.Utf8 as Utf8

    -- | Like @getglobal@, but knows about packages and nested tables. E.g.
    --
    -- > getglobal' "math.sin"
    --
    -- will return the function @sin@ in package @math@.
    getglobal' :: LuaError e => Name -> LuaE e ()
    getglobal' :: forall e. LuaError e => Name -> LuaE e ()
    getglobal' = [Name] -> LuaE e ()
    forall e. LuaError e => [Name] -> LuaE e ()
    getnested ([Name] -> LuaE e ()) -> (Name -> [Name]) -> Name -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Name -> [Name]
    splitdot

    -- | Like @setglobal@, but knows about packages and nested tables. E.g.
    --
    -- > pushstring "0.9.4"
    -- > setglobal' "mypackage.version"
    --
    -- All tables and fields, except for the last field, must exist.
    setglobal' :: LuaError e => Name -> LuaE e ()
    setglobal' :: forall e. LuaError e => Name -> LuaE e ()
    setglobal' Name
    s =
      case [Name] -> [Name]
    forall a. [a] -> [a]
    reverse (Name -> [Name]
    splitdot Name
    s) of
        [] ->
          () -> LuaE e ()
    forall a. a -> LuaE e a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
        [Name
    _] ->
          Name -> LuaE e ()
    forall e. LuaError e => Name -> LuaE e ()
    setglobal Name
    s
        (Name
    lastField : [Name]
    xs) -> do
          [Name] -> LuaE e ()
    forall e. LuaError e => [Name] -> LuaE e ()
    getnested ([Name] -> [Name]
    forall a. [a] -> [a]
    reverse [Name]
    xs)
          StackIndex -> LuaE e ()
    forall e. StackIndex -> LuaE e ()
    pushvalue (CInt -> StackIndex
    nth CInt
    2)
          StackIndex -> Name -> LuaE e ()
    forall e. LuaError e => StackIndex -> Name -> LuaE e ()
    setfield (CInt -> StackIndex
    nth CInt
    2) Name
    lastField
          Int -> LuaE e ()
    forall e. Int -> LuaE e ()
    pop Int
    1

    -- | Gives the list of the longest substrings not containing dots.
    splitdot :: Name -> [Name]
    splitdot :: Name -> [Name]
    splitdot = (String -> Name) -> [String] -> [Name]
    forall a b. (a -> b) -> [a] -> [b]
    map String -> Name
    forall a. IsString a => String -> a
    fromString
      ([String] -> [Name]) -> (Name -> [String]) -> Name -> [Name]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String -> Bool) -> [String] -> [String]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    /= String
    ".")
      ([String] -> [String]) -> (Name -> [String]) -> Name -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Char -> Char -> Bool) -> String -> [String]
    forall a. (a -> a -> Bool) -> [a] -> [[a]]
    groupBy (\Char
    a Char
    b -> Char
    a Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Char
    '.' Bool -> Bool -> Bool
    && Char
    b Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Char
    '.')
      (String -> [String]) -> (Name -> String) -> Name -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> String
    Utf8.toString
      (ByteString -> String) -> (Name -> ByteString) -> Name -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Name -> ByteString
    fromName

    -- | Pushes the value described by the strings to the stack; where the first
    -- value is the name of a global variable and the following strings are the
    -- field values in nested tables.
    getnested :: LuaError e => [Name] -> LuaE e ()
    getnested :: forall e. LuaError e => [Name] -> LuaE e ()
    getnested [] = () -> LuaE e ()
    forall a. a -> LuaE e a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
    getnested (Name
    x:[Name]
    xs) = do
      _ <- Name -> LuaE e Type
    forall e. LuaError e => Name -> LuaE e Type
    getglobal Name
    x
      mapM_ (\Name
    a -> StackIndex -> Name -> LuaE e Type
    forall e. LuaError e => StackIndex -> Name -> LuaE e Type
    getfield StackIndex
    top Name
    a LuaE e Type -> LuaE e () -> LuaE e ()
    forall a b. LuaE e a -> LuaE e b -> LuaE e b
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> StackIndex -> LuaE e ()
    forall e. StackIndex -> LuaE e ()
    remove (CInt -> StackIndex
    nth CInt
    2)) xs