hslua-cli-1.4.1/0000755000000000000000000000000007346545000011567 5ustar0000000000000000hslua-cli-1.4.1/CHANGELOG.md0000644000000000000000000000202107346545000013373 0ustar0000000000000000# Changelog `hslua-cli` uses [PVP Versioning](https://pvp.haskell.org). ## hslua-cli-1.4.1 Released 2023-03-18. - Always start the REPL if the `-i` parameter is given on the command line. This fixes a bug where the REPL would not start if `-v`, `-e` or `-l` where given. ## hslua-cli-1.4.0.1 Released 2023-03-17. - Fix building on Windows. ## hslua-cli-1.4.0 Released 2023-03-16. - Isocline-based REPL: interactive mode is now supported with the help of a new repl built with the isocline library. ## hslua-cli-1.3.0 Released 2023-03-13. - Require hslua-core 2.3. ## hslua-cli-1.2.0 Released 2022-09-27. - The function `runStandalone` now takes two additional arguments, the program name and list command line args. ## hslua-cli-1.1.0 Released 2022-09-26. - Added support for the `LUA_INIT` environment variable. The behavior is as described in the Lua reference manual. - Warnings are now enabled when flag `-W` is given. ## hslua-cli-1.0.0 Released 2022-09-23. - Initial release. hslua-cli-1.4.1/LICENSE0000644000000000000000000000224107346545000012573 0ustar0000000000000000Copyright © 1994-2022 Lua.org, PUC-Rio. Copyright © 2007-2012 Gracjan Polak Copyright © 2012-2015 Ömer Sinan Ağacan Copyright © 2016-2023 Albert Krewinkel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hslua-cli-1.4.1/README.md0000644000000000000000000000117707346545000013054 0ustar0000000000000000hslua-cli ========= Library that allows to embed a standalone Lua interpreter into a larger program. The provided command-line interface is mostly compatible with that of the default `lua` executable that ships with Lua. Example ------- ``` haskell import HsLua.Core as Lua (Exception, openlibs, run) import HsLua.CLI (Settings (..), runStandalone) -- | Run a default Lua interpreter. main :: IO () main = do let settings = Settings { settingsVersionInfo = "\nembedded in MyProgram" , settingsRunner = \action -> run $ do openlibs action } runStandalone @Lua.Exception settings ``` hslua-cli-1.4.1/app/0000755000000000000000000000000007346545000012347 5ustar0000000000000000hslua-cli-1.4.1/app/hslua.hs0000644000000000000000000000201407346545000014014 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {- | Module : Main Copyright : © 2022-2023 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Re-implementation of the standard Lua interpreter. -} module Main (main) where import Control.Monad (when) import HsLua.Core as Lua (Exception, openlibs, pushboolean, registryindex, run, setfield) import HsLua.CLI (EnvBehavior (IgnoreEnvVars), Settings (..), runStandalone) import System.Environment (getArgs, getProgName) -- | Run a default Lua interpreter. main :: IO () main = do let settings = Settings { settingsVersionInfo = "" , settingsRunner = \envBehavior action -> run $ do when (envBehavior == IgnoreEnvVars) $ do pushboolean True setfield registryindex "LUA_NOENV" openlibs action , settingsHistory = Just ".hslua-history" } prg <- getProgName args <- getArgs runStandalone @Lua.Exception settings prg args hslua-cli-1.4.1/hslua-cli.cabal0000644000000000000000000000535607346545000014445 0ustar0000000000000000cabal-version: 2.2 name: hslua-cli version: 1.4.1 synopsis: Command-line interface for Lua description: Provides an embeddable command-line interface for Lua. The interface is compatible with the standard Lua interpreter, i.e., the `lua` executable provided in a default Lua installation. homepage: https://hslua.org/ bug-reports: https://github.com/hslua/hslua/issues license: MIT license-file: LICENSE author: Albert Krewinkel maintainer: Albert Krewinkel copyright: © 2022-2023 Albert Krewinkel category: Foreign build-type: Simple extra-doc-files: README.md CHANGELOG.md tested-with: GHC == 8.4.4 , GHC == 8.6.5 , GHC == 8.8.4 , GHC == 8.10.7 , GHC == 9.0.2 , GHC == 9.2.5 , GHC == 9.4.4 source-repository head type: git location: https://github.com/hslua/hslua subdir: hslua-cli Flag executable description: Compile `hslua` standalone executable. default: False common common-options build-depends: base >= 4.9.1 && < 5 , bytestring , hslua-core >= 2.3 && < 2.4 ghc-options: -Wall -Wcompat -Widentities -Wincomplete-uni-patterns -Wincomplete-record-updates if impl(ghc >= 8.0) ghc-options: -Wredundant-constraints if impl(ghc >= 8.2) ghc-options: -fhide-source-paths if impl(ghc >= 8.4) ghc-options: -Wmissing-export-lists -Wpartial-fields default-language: Haskell2010 library import: common-options hs-source-dirs: src exposed-modules: HsLua.CLI build-depends: base >= 4.11 && < 5 , hslua-marshalling >= 2.2 && < 2.4 , hslua-repl >= 0.1 && < 0.2 , lua >= 2.3 && < 2.4 , text >= 1.2 && < 2.1 if !os(windows) build-depends: unix >= 2.7 && < 2.9 if os(windows) cpp-options: -D_WINDOWS executable hslua import: common-options hs-source-dirs: app main-is: hslua.hs ghc-options: -rtsopts -with-rtsopts=-A8m -threaded if flag(executable) buildable: True build-depends: hslua-cli else buildable: False hslua-cli-1.4.1/src/HsLua/0000755000000000000000000000000007346545000013372 5ustar0000000000000000hslua-cli-1.4.1/src/HsLua/CLI.hs0000644000000000000000000002172007346545000014337 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : HsLua.CLI Copyright : Copyright © 2017-2023 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Embeddable Lua interpreter interface. -} module HsLua.CLI ( -- * Run scripts as program runStandalone , Settings (..) , EnvBehavior (..) ) where import Control.Monad (unless, void, when, zipWithM_) import Data.Bifunctor (first) import Data.ByteString (ByteString) import Data.Foldable (foldl') import Data.Maybe (listToMaybe) import Data.Text (Text) import Foreign.C.String (withCString) import HsLua.Core (LuaE, LuaError) import HsLua.REPL (Config (..), defaultConfig, repl, setup) import System.Console.GetOpt import System.Environment (lookupEnv) import qualified Lua.Constants as Lua import qualified Lua.Primary as Lua import qualified HsLua.Core as Lua import qualified HsLua.Marshalling as Lua import qualified Data.Text as T import qualified Data.Text.IO as T import qualified HsLua.Core.Utf8 as UTF8 #ifndef _WINDOWS import System.Posix.IO (stdOutput) import System.Posix.Terminal (queryTerminal) #endif -- | Whether the program is connected to a terminal istty :: IO Bool #ifdef _WINDOWS istty = pure True #else istty = queryTerminal stdOutput #endif -- | Settings for the Lua command line interface. -- -- If env vars should be ignored, and the interpreter invokes -- @openlibs@, then the registry key @LUA_NOENV@ should be set to @true@ -- before that function is invoked. E.g.: -- -- > runner envBehavior action = run $ do -- > when (envBehavior == IgnoreEnvVars) $ do -- > pushboolean True -- > setfield registryindex "LUA_NOENV" -- > openlibs -- > action -- data Settings e = Settings { settingsVersionInfo :: Text -- ^ Additional version info to present to the user. The current -- Lua version will always be printed. , settingsRunner :: EnvBehavior -> LuaE e () -> IO () -- ^ The Lua interpreter to be used; the first argument indicates -- whether environment variables should be consulted or ignored. , settingsHistory :: Maybe FilePath } -- | Whether environment variables should be consulted or ignored. data EnvBehavior = IgnoreEnvVars | ConsultEnvVars deriving (Eq, Show) -- | Get the Lua interpreter options from the command line. Throws an -- error with usage instructions if parsing fails. getOptions :: String -> [String] -> IO Options getOptions progName rawArgs = do let (actions, args, errs) = getOpt RequireOrder luaOptions rawArgs unless (null errs) . ioError . userError $ let usageHead = "Usage: " ++ progName ++ " [options] [script [args]]" in concat errs ++ usageInfo usageHead luaOptions let (mscript, arg) = first listToMaybe $ splitAt 1 args let opts = foldl' (flip ($)) defaultLuaOpts actions return opts { optScript = mscript , optScriptArgs = arg , optProgName = progName , optAllArgs = rawArgs } -- | Print version information to the terminal. showVersion :: LuaError e => Text -> LuaE e () showVersion extraInfo = do _ <- Lua.getglobal "_VERSION" versionString <- Lua.forcePeek $ Lua.peekText Lua.top `Lua.lastly` Lua.pop 1 Lua.liftIO . T.putStrLn $ versionString `T.append` extraInfo -- | Runs code given on the command line runCode :: LuaError e => LuaCode -> LuaE e () runCode = \case ExecuteCode stat -> do status <- Lua.dostringTrace stat when (status /= Lua.OK) Lua.throwErrorAsException RequireModule g mod' -> do _ <- Lua.getglobal "require" Lua.pushName mod' status <- Lua.pcallTrace 1 1 if status == Lua.OK then Lua.setglobal g else Lua.throwErrorAsException -- -- Standalone -- -- | Uses the first command line argument as the name of a script file -- and tries to run that script in Lua. Falls back to stdin if no file -- is given. Any remaining args are passed to Lua via the global table -- @arg@. runStandalone :: LuaError e => Settings e -- ^ interpreter configuration -> String -- ^ program name (for error messages) -> [String] -- ^ command line arguments -> IO () runStandalone settings progName args = do opts <- getOptions progName args let envVarOpt = if optNoEnv opts then IgnoreEnvVars else ConsultEnvVars settingsRunner settings envVarOpt $ do -- print version info when (optVersion opts) (showVersion $ settingsVersionInfo settings) -- push `arg` table case optScript opts of Just _script -> do let setField i x = Lua.pushString x *> Lua.rawseti (Lua.nth 2) i let nprogargs = length (optAllArgs opts) - length (optScriptArgs opts) let arg = optProgName opts : optAllArgs opts Lua.newtable zipWithM_ setField [-(fromIntegral nprogargs)..] arg Nothing -> do Lua.pushList Lua.pushString (optAllArgs opts) Lua.pushString (optProgName opts) Lua.rawseti (Lua.nth 2) 0 Lua.setglobal "arg" when (optWarnings opts) $ do l <- Lua.state -- turn warnings on Lua.liftIO $ withCString "@on" $ \w -> Lua.lua_warning l w Lua.FALSE -- Run init code. unless (optNoEnv opts) $ do init' <- Lua.liftIO $ lookupEnv "LUA_INIT" (case init' of Just ('@' : filename) -> Lua.dofileTrace (Just filename) Just cmd -> Lua.dostring (UTF8.fromString cmd) Nothing -> return Lua.OK) >>= \case Lua.OK -> pure () _ -> Lua.throwErrorAsException -- run code statements and module loading instructions mapM_ runCode (reverse $ optExecute opts) let nargs = fromIntegral . length $ optScriptArgs opts let startREPL = do setup defaultConfig { replHistory = settingsHistory settings , replInfo = replInfo defaultConfig `T.append` settingsVersionInfo settings } void repl let handleScriptResult = \case Lua.OK -> do mapM_ Lua.pushString (optScriptArgs opts) status <- Lua.pcallTrace nargs Lua.multret when (status /= Lua.OK) Lua.throwErrorAsException when (optInteractive opts) startREPL _ -> Lua.throwErrorAsException tty <- Lua.liftIO istty case optScript opts of Just "-" -> -- load from stdin Lua.loadfile Nothing >>= handleScriptResult Just script -> Lua.loadfile (Just script) >>= handleScriptResult _ | optInteractive opts -> do startREPL _ | optVersion opts || not (null (optExecute opts)) -> pure () _ | tty -> do startREPL _ -> do -- load script from stdin Lua.loadfile Nothing >>= handleScriptResult -- | Code to execute on startup. data LuaCode = ExecuteCode ByteString | RequireModule Lua.Name Lua.Name -- | Lua runner command line options. data Options = Options { optNoEnv :: Bool -- ^ Ignore environment variables , optInteractive :: Bool -- ^ Interactive , optVersion :: Bool -- ^ Show version info , optWarnings :: Bool -- ^ Whether warnings are enabled , optExecute :: [LuaCode] -- ^ code to execute, in reverse order , optProgName :: String -- ^ program name , optAllArgs :: [String] -- ^ all arguments , optScript :: Maybe String -- ^ script name, if any , optScriptArgs :: [String] -- ^ arguments passed to the script } defaultLuaOpts :: Options defaultLuaOpts = Options { optNoEnv = False , optInteractive = False , optVersion = False , optWarnings = False , optExecute = mempty , optProgName = mempty , optAllArgs = mempty , optScript = Nothing , optScriptArgs = mempty } -- | Lua command line options. luaOptions :: [OptDescr (Options -> Options)] luaOptions = [ Option "e" [] (flip ReqArg "stat" $ \stat opt -> let code = ExecuteCode $ UTF8.fromString stat in opt{ optExecute = code:optExecute opt }) "execute string 'stat'" , Option "i" [] (NoArg $ \opt -> opt { optInteractive = True }) "interactive mode -- currently not supported" , Option "l" [] (flip ReqArg "mod" $ \mod' opt -> let toName = Lua.Name . UTF8.fromString code = case break (== '=') mod' of (glb, '=':m) -> RequireModule (toName glb) (toName m) (glb, _ ) -> RequireModule (toName glb) (toName glb) in opt{ optExecute = code:optExecute opt }) (unlines [ "require library 'mod' into global 'mod';" , "if 'mod' has the pattern 'g=module', then" , "require library 'module' into global 'g'" ]) , Option "v" [] (NoArg $ \opt -> opt { optVersion = True }) "show version information" , Option "E" [] (NoArg $ \opt -> opt { optNoEnv = True }) "ignore environment variables -- partially supported" , Option "W" [] (NoArg $ \opt -> opt { optWarnings = True }) "turn warnings on -- currently not supported" ]