{-|
Module      : HsLua.Util
Copyright   : © 2007–2012 Gracjan Polak;
              © 2012–2016 Ömer Sinan Ağacan;
              © 2017-2022 Albert Krewinkel
License     : MIT
Maintainer  : Albert Krewinkel <tarleb+hslua@zeitkraut.de>
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' :: 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' :: Name -> LuaE e ()
setglobal' Name
s =
  case [Name] -> [Name]
forall a. [a] -> [a]
reverse (Name -> [Name]
splitdot Name
s) of
    [] ->
      () -> LuaE e ()
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 :: [Name] -> LuaE e ()
getnested [] = () -> LuaE e ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
getnested (Name
x:[Name]
xs) = do
  Type
_ <- Name -> LuaE e Type
forall e. LuaError e => Name -> LuaE e Type
getglobal Name
x
  (Name -> LuaE e ()) -> [Name] -> LuaE e ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
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 (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)) [Name]
xs