packages feed

dhall-bash (empty) → 1.0.0

raw patch · 5 files changed

+456/−0 lines, 5 filesdep +basedep +bytestringdep +containerssetup-changed

Dependencies added: base, bytestring, containers, dhall, dhall-bash, neat-interpolation, optparse-generic, shell-escape, text, text-format, trifecta, vector

Files

+ LICENSE view
@@ -0,0 +1,24 @@+Copyright (c) 2017 Gabriel Gonzalez+All rights reserved.++Redistribution and use in source and binary forms, with or without modification,+are permitted provided that the following conditions are met:+    * Redistributions of source code must retain the above copyright notice,+      this list of conditions and the following disclaimer.+    * Redistributions in binary form must reproduce the above copyright notice,+      this list of conditions and the following disclaimer in the documentation+      and/or other materials provided with the distribution.+    * Neither the name of Gabriel Gonzalez nor the names of other contributors+      may be used to endorse or promote products derived from this software+      without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ dhall-bash.cabal view
@@ -0,0 +1,54 @@+Name: dhall-bash+Version: 1.0.0+Cabal-Version: >=1.8.0.2+Build-Type: Simple+Tested-With: GHC == 7.10.2, GHC == 8.0.1+License: BSD3+License-File: LICENSE+Copyright: 2017 Gabriel Gonzalez+Author: Gabriel Gonzalez+Maintainer: Gabriel439@gmail.com+Bug-Reports: https://github.com/Gabriel439/Haskell-Dhall-Bash-Library/issues+Synopsis: Compile Dhall to Bash+Description:+    Use this package if you want to compile Dhall expressions to Bash.+    You can use this package as a library or an executable:+    .+    * See the "Dhall.Bash" module if you want to use this package as a library+    .+    * Use the @dhall-to-bash@ if you want an executable+    .+    The "Dhall.Bash" module also contains instructions for how to use this+    package+Category: Compiler+Source-Repository head+    Type: git+    Location: https://github.com/Gabriel439/Haskell-Dhall-Bash-Library++Library+    Hs-Source-Dirs: src+    Build-Depends:+        base               >= 4.8.0.0 && < 5   ,+        bytestring                       < 0.11,+        containers                       < 0.6 ,+        dhall              >= 1.0.1   && < 1.3 ,+        neat-interpolation               < 0.4 ,+        shell-escape                     < 0.3 ,+        text-format                      < 0.4 ,+        text               >= 0.2     && < 1.3 ,+        vector             >= 0.3     && < 0.13+    Exposed-Modules: Dhall.Bash+    GHC-Options: -Wall++Executable dhall-to-bash+    Hs-Source-Dirs: exec+    Main-Is: Main.hs+    Build-Depends:+        base             >= 4.8.0.0  && < 5   ,+        bytestring                      < 0.11,+        dhall            >= 1.0.1    && < 1.3 ,+        dhall-bash                            ,+        optparse-generic >= 1.1.1    && < 1.2 ,+        trifecta         >= 1.6      && < 1.7 ,+        text             >= 0.11.1.0 && < 1.3+    GHC-Options: -Wall
+ exec/Main.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE TypeOperators     #-}++module Main where++import Control.Exception (SomeException)+import Data.ByteString (ByteString)+import Options.Generic (Generic, ParseRecord, type (<?>)(..))+import System.Exit (ExitCode(..))+import Text.Trifecta.Delta (Delta(..))++import qualified Control.Exception+import qualified Data.ByteString+import qualified Data.Text.Lazy.IO+import qualified Dhall+import qualified Dhall.Bash+import qualified Dhall.Import+import qualified Dhall.Parser+import qualified Dhall.TypeCheck+import qualified GHC.IO.Encoding+import qualified Options.Generic+import qualified System.Exit+import qualified System.IO++data Options = Options+    { explain :: Bool+        <?> "Explain error messages in detail"+    , declare :: Maybe ByteString+        <?> "Declare the given variable as a statement instead of an expression"+    } deriving (Generic, ParseRecord)++main :: IO ()+main = handle (do+    GHC.IO.Encoding.setLocaleEncoding GHC.IO.Encoding.utf8+    Options {..} <- Options.Generic.getRecord "Compile Dhall to Bash"++    (if unHelpful explain then Dhall.detailed else id) (do+        inText <- Data.Text.Lazy.IO.getContents++        expr <- case Dhall.Parser.exprFromText (Directed "(stdin)" 0 0 0 0) inText of+            Left  err  -> Control.Exception.throwIO err+            Right expr -> return expr++        expr' <- Dhall.Import.load expr+        case Dhall.TypeCheck.typeOf expr' of+            Left  err -> Control.Exception.throwIO err+            Right _   -> return ()++        bytes <- case unHelpful declare of+            Nothing  -> do+                case Dhall.Bash.dhallToExpression expr' of+                    Left  err   -> Control.Exception.throwIO err+                    Right bytes -> return bytes+            Just var -> do+                case Dhall.Bash.dhallToStatement expr' var of+                    Left  err   -> Control.Exception.throwIO err+                    Right bytes -> return bytes+        Data.ByteString.putStr bytes ))++handle :: IO a -> IO a+handle = Control.Exception.handle handler+  where+    handler :: SomeException -> IO a+    handler e = case Control.Exception.fromException e of+        Just ExitSuccess -> do+            Control.Exception.throwIO e+        _ -> do+            System.IO.hPutStrLn System.IO.stderr ""+            System.IO.hPrint    System.IO.stderr e+            System.Exit.exitFailure
+ src/Dhall/Bash.hs view
@@ -0,0 +1,302 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings  #-}+{-# LANGUAGE QuasiQuotes        #-}++{-| This library exports two utilities for compiling Dhall expressions to Bash:++    * `dhallToExpression`, which emits a Bash expression (i.e. a valid+      right-hand side for an assignment)++    * `dhallToStatement`, which emits a Bash @declare@ or @unset@ statement+      suitable for use with `eval`++    `dhallToExpression` only supports the conversion of primitive values, such+    as:++    * @Bool@ - which translates to a string that is either @"true"@ or @"false"@+    * @Natural@ - which translates to a Bash integer+    * @Integer@ - which translates to a Bash integer+    * @Text@ - which translates to a Bash string (properly escaped if necessary)++    The @dhall-to-bash@ executable by default tries to compile Dhall expressions+    to Bash expressions using the `dhallToExpression` function.  For example:++> $ dhall-to-bash <<< 'True'+> true+> $ dhall-to-bash <<< 'False'+> false+> $ dhall-to-bash <<< '1'+> 1+> $ dhall-to-bash <<< '+1'+> 1+> $ dhall-to-bash <<< '"ABC"'+> ABC+> $ dhall-to-bash <<< '" X "'+> $' X '+> $ dhall-to-bash <<< 'Natural/even +100'+> true++    The output of `dhallToExpression` is a valid Bash expression that can be+    embedded anywhere Bash expressions are valid, such as the right-hand side of+    an assignment statement:++> $ FOO=$(dhall-to-bash <<< 'List/length Integer [1, 2, 3]')+> $ echo "${FOO}"+> 3++    `dhallToStatement` supports a wider range of expressions by also adding+    support for:++    * @Optional@ - which translates to a variable which is either set or unset+    * @List@ - which translates to a Bash array+    * records - which translate to Bash associative arrays++    The @dhall-to-bash@ executable can emit a statement instead of an expression+    if you add the @--declare@ flag specifying which variable to set or unset.+    For example:++> $ dhall-to-bash --declare FOO <<< '[] : Optional Integer'+> unset FOO+> $ dhall-to-bash --declare FOO <<< '[1] : Optional Integer'+> declare -r -i FOO=1+> $ dhall-to-bash --declare FOO <<< '[[1] : Optional Integer] : Optional (Optional Integer)'+> declare -r -i FOO=1+> $ dhall-to-bash --declare FOO <<< '[[] : Optional Integer] : Optional (Optional Integer)'+> unset FOO+> $ dhall-to-bash --declare FOO <<< '[1, 2, 3]'+> declare -r -a FOO=(1 2 3)+> $ dhall-to-bash --declare FOO <<< '{ bar = 1, baz = True }'+> declare -r -A FOO=([bar]=1 [baz]=true)++    The output of `dhallToExpression` is either a @declare@ or @unset@ Bash+    statement that you can pass to @eval@:++> $ eval $(dhall-to-bash --declare FOO <<< '{ bar = 1, baz = True }')+> $ echo "${FOO[bar]}"+> 1+> $ echo "${FOO[baz]}"+> true++    @dhall-to-bash@ declares variables read-only (i.e. @-r@) to prevent you from+    accidentally overwriting, deleting or mutating variables:++> $ eval $(dist/build/dhall-to-bash/dhall-to-bash --declare BAR <<< '1')+> $ echo "${BAR"}+> 1+> $ unset BAR+> bash: unset: BAR: cannot unset: readonly variable+> $ eval $(dist/build/dhall-to-bash/dhall-to-bash --declare BAR <<< '2')+> bash: declare: BAR: readonly variable++-}++module Dhall.Bash (+    -- * Dhall to Bash+      dhallToExpression+    , dhallToStatement++    -- * Exceptions+    , ExpressionError(..)+    , StatementError(..)+    ) where++import Control.Exception (Exception)+import Data.Bifunctor (first)+import Data.ByteString+import Data.Monoid ((<>))+import Data.Typeable (Typeable)+import Dhall.Core (Expr(..))+import Dhall.TypeCheck++import qualified Data.Foldable+import qualified Data.Map+import qualified Data.Text+import qualified Data.Text.Buildable+import qualified Data.Text.Encoding+import qualified Data.Text.Lazy+import qualified Data.Text.Lazy.Builder+import qualified Data.Vector+import qualified Dhall.Core+import qualified NeatInterpolation+import qualified Text.ShellEscape++_ERROR :: Data.Text.Text+_ERROR = "\ESC[1;31mError\ESC[0m"++{-| This is the exception type for errors that might arise when translating+    Dhall expressions to Bash statements++    Because the majority of Dhall language features do not easily translate to+    Bash this just returns the expression that failed+-}+data StatementError+    = UnsupportedStatement (Expr X X)+    | UnsupportedSubexpression (Expr X X)+    deriving (Typeable)++instance Show StatementError where+    show (UnsupportedStatement e) =+        Data.Text.unpack [NeatInterpolation.text|+$_ERROR: Cannot translate to a Bash statement++Explanation: Only primitive values, records, ❰List❱s, and ❰Optional❱ values can+be translated from Dhall to a Bash statement++The following Dhall expression could not be translated to a Bash statement:++↳ $txt+|]+      where+        txt = Data.Text.Lazy.toStrict (Dhall.Core.pretty e)+    show (UnsupportedSubexpression e) =+        -- Carefully note: No tip suggesting `--declare` since it won't work+        -- here (and the user is already using `--declare`)+        Data.Text.unpack [NeatInterpolation.text|+$_ERROR: Cannot translate to a Bash expression++Explanation: Only primitive values can be translated from Dhall to a Bash+expression++The following Dhall expression could not be translated to a Bash expression:++↳ $txt+|]+      where+        txt = Data.Text.Lazy.toStrict (Dhall.Core.pretty e)++instance Exception StatementError++{-| This is the exception type for errors that might arise when translating+    Dhall expressions to Bash expressions++    Because the majority of Dhall language features do not easily translate to+    Bash this just returns the expression that failed+-}+data ExpressionError = UnsupportedExpression (Expr X X) deriving (Typeable)++instance Show ExpressionError where+    show (UnsupportedExpression e) =+        Data.Text.unpack [NeatInterpolation.text|+$_ERROR: Cannot translate to a Bash expression++Explanation: Only primitive values can be translated from Dhall to a Bash+expression++The following Dhall expression could not be translated to a Bash expression:++↳ $txt$tip+|]+      where+        txt = Data.Text.Lazy.toStrict (Dhall.Core.pretty e)++        tip = case e of+            OptionalLit _ _ -> "\n\n" <> [NeatInterpolation.text|+Tip: You can convert an ❰Optional❱ value to a Bash statement using the --declare+flag+|]+            ListLit _ _ -> "\n\n" <> [NeatInterpolation.text|+Tip: You can convert a ❰List❱ to a Bash statement using the --declare flag+|]+            RecordLit _ -> "\n\n" <> [NeatInterpolation.text|+Tip: You can convert a record to a Bash statement using the --declare flag+|]+            _ -> ""++instance Exception ExpressionError++{-| Compile a Dhall expression to a Bash statement that @declare@s or @unset@s a+    a variable of your choice++    This only supports:++    * @Bool@s+    * @Natural@s+    * @Integer@s+    * @Text@s+    * @Optional@s+    * @List@s+    * records+ -}+dhallToStatement+    :: Expr s X+    -- ^ Dhall expression to compile+    -> ByteString+    -- ^ Variable to @declare@ or @unset@+    -> Either StatementError ByteString+    -- ^ Bash statement or compile failure+dhallToStatement expr0 var0 = go (Dhall.Core.normalize expr0)+  where+    var = Text.ShellEscape.bytes (Text.ShellEscape.bash var0)++    adapt (UnsupportedExpression e) = UnsupportedSubexpression e++    go (BoolLit a) = do+        go (TextLit (if a then "true" else "false"))+    go (NaturalLit a) = do+        go (IntegerLit (fromIntegral a))+    go (IntegerLit a) = do+        e <- first adapt (dhallToExpression (IntegerLit a))+        let bytes = "declare -r -i " <> var <> "=" <> e+        return bytes+    go (TextLit a) = do+        e <- first adapt (dhallToExpression (TextLit a))+        let bytes = "declare -r " <> var <> "=" <> e+        return bytes+    go (ListLit _ bs) = do+        bs' <- first adapt (mapM dhallToExpression bs)+        let bytes+                =   "declare -r -a "+                <>  var+                <>  "=("+                <>  Data.ByteString.intercalate " " (Data.Foldable.toList bs')+                <>  ")"+        return bytes+    go (OptionalLit _ bs) = do+        if Data.Vector.null bs+            then do+                let bytes = "unset " <> var+                return bytes+            else go (Data.Vector.head bs)+    go (RecordLit a) = do+        let process (k, v) = do+                v' <- dhallToExpression v+                let bytes = Data.Text.Encoding.encodeUtf8 (Data.Text.Lazy.toStrict k)+                let k'    = Text.ShellEscape.bytes (Text.ShellEscape.bash bytes)+                return ("[" <> k' <> "]=" <> v')+        kvs' <- first adapt (traverse process (Data.Map.toList a))+        let bytes+                =   "declare -r -A "+                <>  var+                <>  "=("+                <>  Data.ByteString.intercalate " " kvs'+                <>  ")"+        return bytes+    go e = Left (UnsupportedStatement e)++{-| Compile a Dhall expression to a Bash expression++    This only supports:++    * @Bool@s+    * @Natural@s+    * @Integer@s+    * @Text@s+ -}+dhallToExpression+    :: Expr s X+    -- ^ Dhall expression to compile+    -> Either ExpressionError ByteString+    -- ^ Bash expression or compile failure+dhallToExpression expr0 = go (Dhall.Core.normalize expr0)+  where+    go (BoolLit a) = do+        go (TextLit (if a then "true" else "false"))+    go (NaturalLit a) = do+        go (IntegerLit (fromIntegral a))+    go (IntegerLit a) = do+        go (TextLit (Data.Text.Buildable.build a))+    go (TextLit a) = do+        let text  = Data.Text.Lazy.Builder.toLazyText a+        let bytes = Data.Text.Encoding.encodeUtf8 (Data.Text.Lazy.toStrict text)+        return (Text.ShellEscape.bytes (Text.ShellEscape.bash bytes))+    go e = Left (UnsupportedExpression e)