Browse Source

example lua-as-config program

Getty Ritter 6 months ago
commit
b9203621b9
4 changed files with 112 additions and 0 deletions
  1. 20 0
      .gitignore
  2. 22 0
      cfg.cabal
  3. 8 0
      sample.lua
  4. 62 0
      src/Main.hs

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
+dist
+dist-*
+*~
+cabal-dev
+*.o
+*.hi
+*.chi
+*.chs.h
+*.dyn_o
+*.dyn_hi
+.hpc
+.hsenv
+.cabal-sandbox/
+cabal.sandbox.config
+*.prof
+*.aux
+*.hp
+*.eventlog
+cabal.project.local
+.ghc.environment.*

+ 22 - 0
cfg.cabal

@@ -0,0 +1,22 @@
+name: cfg
+version: 0.1.0.0
+-- synopsis:
+-- description:
+license: BSD3
+author: Getty Ritter <tristero@infinitenegativeutility.com>
+maintainer: Getty Ritter <tristero@infinitenegativeutility.com>
+copyright: @2021 Getty Ritter
+-- category:
+build-type: Simple
+cabal-version: >=1.14
+
+executable cfg
+  hs-source-dirs: src
+  main-is: Main.hs
+  default-language: Haskell2010
+  default-extensions: ScopedTypeVariables
+  ghc-options: -Wall
+  build-depends: base >=4.7 && <5
+               , hslua
+               , bytestring
+               , containers

+ 8 - 0
sample.lua

@@ -0,0 +1,8 @@
+-- a simple config file that doesn't do any computation
+host = "localhost"
+port = 8080
+files = {"foo.txt", "bar.jpeg"}
+-- ... but maybe a little bit, just to show off what can be done
+for i = 1,3 do
+  table.insert(files, "file_" .. i .. ".dat")
+end

+ 62 - 0
src/Main.hs

@@ -0,0 +1,62 @@
+{-# 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