packages feed

shell-monad 0.1.0 → 0.2.0

raw patch · 4 files changed

+108/−38 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Control.Monad.Shell: (-&&-) :: Script () -> Script () -> Script ()
+ Control.Monad.Shell: (-||-) :: Script () -> Script () -> Script ()
+ Control.Monad.Shell: NamedLike :: Text -> NamedLike
+ Control.Monad.Shell: class NameHinted h
+ Control.Monad.Shell: instance NameHinted ()
+ Control.Monad.Shell: instance NameHinted (Maybe Text)
+ Control.Monad.Shell: instance NameHinted NamedLike
+ Control.Monad.Shell: newtype NamedLike
- Control.Monad.Shell: func :: ShellCmd callfunc => Script () -> Script callfunc
+ Control.Monad.Shell: func :: (NameHinted namehint, ShellCmd callfunc) => namehint -> Script () -> Script callfunc
- Control.Monad.Shell: newVar :: Text -> Script Var
+ Control.Monad.Shell: newVar :: NameHinted namehint => namehint -> Script Var
- Control.Monad.Shell: newVarContaining :: Text -> Text -> Script Var
+ Control.Monad.Shell: newVarContaining :: NameHinted namehint => Text -> namehint -> Script Var
- Control.Monad.Shell: takeParameter :: Script Var
+ Control.Monad.Shell: takeParameter :: NameHinted namehint => namehint -> Script Var

Files

CHANGELOG view
@@ -1,10 +1,18 @@-shell-monad (0.1.0) UNRELEASED; urgency=medium+shell-monad (0.2.0) unstable; urgency=medium +  * newVar, newVarContaining, takeParameter, and func all+    now take a namehint parameter, which can be () or (NameHinted "foo").+  * Added -&&- and -||-++ -- Joey Hess <id@joeyh.name>  Thu, 25 Dec 2014 20:19:35 -0400++shell-monad (0.1.0) unstable; urgency=medium+   * Added positionalParameters and takeParameter.   * Shell functions can be called with parameters.   * String is now an instance of CmdArg - -- Joey Hess <id@joeyh.name>  Thu, 25 Dec 2014 12:17:23 -0400+ -- Joey Hess <id@joeyh.name>  Thu, 25 Dec 2014 13:04:35 -0400  shell-monad (0.0.3) unstable; urgency=medium 
Control/Monad/Shell.hs view
@@ -20,6 +20,8 @@ 	Output(..), 	Val(..), 	comment,+	NamedLike(..),+	NameHinted, 	newVar, 	newVarContaining, 	globalVar,@@ -27,6 +29,8 @@ 	takeParameter, 	func, 	(-|-),+	(-&&-),+	(-||-), 	forCmd, 	whileCmd, 	ifCmd,@@ -78,6 +82,8 @@ 	| HereDocBody L.Text -- ^ the body of a here-doc 	| Subshell L.Text [Expr] -- ^ expressions run in a sub-shell 	| Pipe Expr Expr -- ^ Piping the first Expr to the second Expr+	| And Expr Expr -- ^ &&+	| Or Expr Expr -- ^ ||  -- | Indents an Expr indent :: Expr -> Expr@@ -86,6 +92,8 @@ indent (HereDocBody t) = HereDocBody t -- cannot indent indent (Subshell i l) = Subshell ("\t" <> i) (map indent l) indent (Pipe e1 e2) = Pipe (indent e1) (indent e2)+indent (And e1 e2) = And (indent e1) (indent e2)+indent (Or e1 e2) = Or (indent e1) (indent e2)  -- | Shell script monad. newtype Script a = Script (Env -> ([Expr], Env, a))@@ -144,6 +152,8 @@ 	fmt (HereDocBody t) = t 	fmt (Subshell i l) = i <> "(\n" <> L.intercalate "\n" (map (fmt . indent) l) <> "\n" <> i <> ")" 	fmt (Pipe e1 e2) = fmt e1 <> " | " <> fmt e2+	fmt (And e1 e2) = fmt e1 <> " && " <> fmt e2+	fmt (Or e1 e2) = fmt e1 <> " || " <> fmt e2  -- | Generates a single line of shell code. linearScript :: Script f -> L.Text@@ -159,6 +169,8 @@ 	fmt (HereDocBody _) = "" 	fmt (Subshell i l) = i <> "(" <> L.intercalate "; " (map (fmt . indent) l) <> i <> ")" 	fmt (Pipe e1 e2) = fmt e1 <> " | " <> fmt e2+	fmt (And e1 e2) = fmt e1 <> " && " <> fmt e2+	fmt (Or e1 e2) = fmt e1 <> " || " <> fmt e2  -- | Adds a shell command to the script. run :: L.Text -> [L.Text] -> Script ()@@ -244,31 +256,52 @@ comment :: L.Text -> Script () comment = add . Comment +-- | Suggests that a shell variable or function have its name contain+-- the specified Text.+newtype NamedLike = NamedLike L.Text++-- | Class of values that provide a hint for the name to use for a shell+-- variable or function.+--+-- To skip providing a hint, use '()'.+-- To provide a hint, use '(NamedLike \"name\")'.+class NameHinted h where+	hinted :: (Maybe L.Text -> a) -> h -> a++instance NameHinted () where+	hinted f _ = f Nothing++instance NameHinted NamedLike where+	hinted f (NamedLike h) = f (Just h)++instance NameHinted (Maybe L.Text) where+	hinted = id+ -- | Defines a new shell variable. ----- The name of the variable that appears in the shell script will be based--- on provided name (which can be mempty), but each call to newVar will--- generate a new, unique variable name.-newVar-	:: L.Text -- ^ base of variable name-	-> Script Var-newVar basename = Script $ \env ->-	let v = go env (0 :: Integer)+-- Each call to newVar will generate a new, unique variable name.+--+-- The namehint can influence this name, but is modified to ensure+-- uniqueness.+newVar :: (NameHinted namehint) => namehint -> Script Var+newVar = hinted $ \namehint -> Script $ \env ->+	let v = go namehint env (0 :: Integer) 	in ([], modifyEnvVars env (S.insert v), v)   where-	go env x-		| S.member v (envVars env) = go env (succ x)+	go namehint env x+		| S.member v (envVars env) = go namehint env (succ x) 		| otherwise = v 	  where-		v = Var $ "_" <> basename <> L.pack (show (x + 1))+		v = Var $ "_"+			<> genvarname namehint+			<> if x == 0 then "" else L.pack (show (x + 1))+	+	genvarname = maybe "v" (L.filter isAlpha)  -- | Creates a new shell variable, with an initial value.-newVarContaining-	:: L.Text -- ^ base of variable name-	-> L.Text -- ^ value-	-> Script Var-newVarContaining basename value = do-	v@(Var name) <- newVar basename+newVarContaining :: (NameHinted namehint) => L.Text -> namehint -> Script Var+newVarContaining value = hinted $ \namehint -> do+	v@(Var name) <- newVar namehint 	Script $ \env -> ([Cmd (name <> "=" <> getQ (quote value))], env, v)  -- | Gets a Var that refers to a global variable, such as PATH@@ -297,9 +330,9 @@ -- > removefirstfile = script $ do -- >   cmd "rm" =<< takeParameter -- >   cmd "echo" "remaining parameters:" positionalParameters-takeParameter :: Script Var-takeParameter = do-	p@(Var name) <- newVar "param"+takeParameter :: (NameHinted namehint) => namehint -> Script Var+takeParameter = hinted $ \namehint -> do+	p@(Var name) <- newVar namehint 	Script $ \env -> ([Cmd (name <> "=\"$1\""), Cmd "shift"], env, p)  -- | Defines a shell function, and returns an action that can be run to@@ -309,6 +342,11 @@ -- Typically, it will make sense to specify a more concrete type -- when defining the shell function. --+-- The shell function will be given a unique name, that is not used by any+-- other shell function. The namehint can be used to influence the contents+-- of the function name, which makes for more readable generated shell+-- code.+-- -- For example: -- -- > demo = script $ do@@ -318,23 +356,30 @@ -- >    hohoho (Val 3) -- >  -- > mkHohoho :: Script (Val Int -> Script ())--- > mkHohoho = func $ do+-- > mkHohoho = func (NamedLike "hohoho") $ do -- >    num <- takeParameter -- >    forCmd (cmd "seq" "1" num) $ \_n -> -- >       cmd "echo" "Ho, ho, ho!" "Merry xmas!"-func :: ShellCmd callfunc => Script () -> Script callfunc-func s = Script $ \env ->-	let f = go env (0 :: Integer)+func+	:: (NameHinted namehint, ShellCmd callfunc)+	=> namehint+	-> Script ()+	-> Script callfunc+func h s = flip hinted h $ \namehint -> Script $ \env ->+	let f = go (genfuncname namehint) env (0 :: Integer) 	    env' = modifyEnvFuncs env (S.insert f) 	    (ls, env'') = eval env' s 	in (definefunc f ls, env'', callfunc f)   where-	basename = "p"-	go env x-		| S.member f (envFuncs env) = go env (succ x)+	go basename env x+		| S.member f (envFuncs env) = go basename env (succ x) 		| otherwise = f 	  where-		f = Func $ basename <> L.pack (show (x + 1))+		f = Func $ "_"+			<> basename+			<> if x == 0 then "" else L.pack (show (x + 1))+	+	genfuncname = maybe "p" (L.filter isAlpha)  	definefunc (Func f) ls = (Cmd $ f <> " () { :") : map indent ls ++ [ Cmd "}" ] @@ -342,10 +387,21 @@  -- | Pipes together two Scripts. (-|-) :: Script () -> Script () -> Script ()-a -|- b = do+(-|-) = combine Pipe++-- | ANDs two Scripts.+(-&&-) :: Script () -> Script () -> Script ()+(-&&-) = combine And++-- | ORs two Scripts.+(-||-) :: Script () -> Script () -> Script ()+(-||-) = combine Or++combine :: (Expr -> Expr -> Expr) -> Script () -> Script () -> Script ()+combine f a b = do 	alines <- runM a 	blines <- runM b-	add $ Pipe (toExp alines) (toExp blines)+	add $ f (toExp alines) (toExp blines)   where 	toExp [e] = e 	toExp l = Subshell L.empty l@@ -356,7 +412,7 @@ -- The action is run for each part, passed a Var containing the part. forCmd :: Script () -> (Var -> Script ()) -> Script () forCmd c a = do-	v@(Var vname) <- newVar "x"+	v@(Var vname) <- newVar (NamedLike "x") 	s <- toLinearScript <$> runM c 	add $ Cmd $ "for " <> vname <> " in $(" <> s <> ")" 	block "do" (a v)@@ -429,9 +485,15 @@ ignoreFailure :: Script () -> Script () ignoreFailure s = runM s >>= mapM_ (add . go)   where-	go (Cmd t) = Cmd $ t <> " || true"+	go c@(Cmd _) = Or c true 	go c@(Comment _) = c 	go c@(HereDocBody _) = c 	go (Subshell i l) = Subshell i (map go l) 	-- Assumes pipefail is not set. 	go (Pipe e1 e2) = Pipe e1 (go e2)+	-- Note that in shell, a && b || true will result in true;+	-- there is no need for extra parens.+	go c@(And _ _) = Or c true+	go (Or e1 e2) = Or e1 (go e2)++	true = Cmd "true"
examples/santa.hs view
@@ -21,8 +21,8 @@ 	hohoho (Val 3)  mkHohoho :: Script (Val Int -> Script ())-mkHohoho = func $ do-	num <- takeParameter+mkHohoho = func (NamedLike "hohoho") $ do+	num <- takeParameter (NamedLike "num") 	forCmd (cmd "seq" "1" num) $ \_n -> 		cmd "echo" "Ho, ho, ho!" "Merry xmas!" @@ -32,6 +32,6 @@ promptFor :: T.Text -> (Var -> Script ()) -> Script () promptFor prompt cont = do 	cmd "printf" (prompt <> " ")-	var <- newVar "prompt"+	var <- newVar (NamedLike prompt) 	readVar var 	cont var
shell-monad.cabal view
@@ -1,5 +1,5 @@ Name: shell-monad-Version: 0.1.0+Version: 0.2.0 Cabal-Version: >= 1.8 License: BSD3 Maintainer: Joey Hess <id@joeyh.name>