{-# LANGUAGE NamedFieldPuns #-} module Main where import qualified Control.Exception as Exn import qualified Foreign.Lua as Lua data Config = Config { cfgHost :: String, cfgPort :: Int, cfgFiles :: [String] } deriving (Show) readConfig :: FilePath -> IO Config readConfig configPath = -- set up a new Lua state, and make sure we close it at the end Exn.bracket Lua.newstate Lua.close $ \st -> -- we should set up a mechanism to convert Lua/Haskell errors into -- one another, but that seems like a lot of work here, so this -- just panics whenever any errors happen, hence `unsafeRunWith` Lua.unsafeRunWith st $ do -- this loads the stdlib into Lua, but you probably don't -- actually want this in a config file! (it allows e.g. file IO -- and stuff) Lua.openlibs -- load the actual config path. this should check error -- conditions, but eh. _ <- Lua.loadfile configPath -- that simply loads the code as a zero-argument function: now -- call it Lua.call 0 0 -- use the `readGlobal` helper to extract the globals defined by -- the config file... cfgHost <- readGlobal "host" cfgPort <- readGlobal "port" cfgFiles <- readGlobal "files" -- and return the values return Config {cfgHost, cfgPort, cfgFiles} -- | Read the global variable @name@ into a Haskell value readGlobal :: Lua.Peekable t => String -> Lua.Lua t readGlobal name = do -- this pushes the global named `name` onto the top of the stack Lua.getglobal name -- this finds out what index is at the top of the stack idx <- Lua.gettop -- this uses the `peek` functionality, already parameterized by a -- typeclass, to convert the value at the given index into the -- appropriate Haskell type value <- Lua.peek idx -- pop the global, now that we've already got it in Haskell-land Lua.pop 1 -- ...and return it return value main :: IO () main = do cfg <- readConfig "sample.lua" print cfg