Add dhall-format executable (#137)

Fixes https://github.com/dhall-lang/dhall-lang/issues/8

This takes a Dhall expression on standard input and outputs the same
expression to standard output except pretty-printed in a standard format
This commit is contained in:
Gabriel Gonzalez 2017-09-17 08:18:15 -07:00 committed by GitHub
parent 8a4e744fb7
commit 5f7d46b179
7 changed files with 850 additions and 9 deletions

View File

@ -1,7 +1,7 @@
{ mkDerivation, ansi-wl-pprint, base, bytestring, case-insensitive
, charset, containers, contravariant, exceptions, http-client
, http-client-tls, lens, optparse-generic, parsers, stdenv
, system-fileio, system-filepath, tasty, tasty-hunit, text
, http-client-tls, lens, optparse-generic, parsers, prettyprinter
, stdenv, system-fileio, system-filepath, tasty, tasty-hunit, text
, text-format, transformers, trifecta, unordered-containers, vector
}:
mkDerivation {
@ -13,8 +13,8 @@ mkDerivation {
libraryHaskellDepends = [
ansi-wl-pprint base bytestring case-insensitive charset containers
contravariant exceptions http-client http-client-tls lens parsers
system-fileio system-filepath text text-format transformers
trifecta unordered-containers vector
prettyprinter system-fileio system-filepath text text-format
transformers trifecta unordered-containers vector
];
executableHaskellDepends = [ base optparse-generic text trifecta ];
testHaskellDepends = [

93
dhall-format/Main.hs Normal file
View File

@ -0,0 +1,93 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
{-| Utility executable for pretty-printing Dhall code
You typically want to use this to either:
* improve the readability of Dhall code (either written or generated)
* automatically format your Dhall code to avoid stylistic debates
Note that this does not yet support:
* Preserving comments (currently, this just removes them)
* Preserving multi-line strings (this reduces them to ordinary strings)
* Preserving string interpolation (this expands interpolation to @++@)
See the @Dhall.Tutorial@ module for example usage
-}
module Main where
import Control.Exception (SomeException)
import Control.Monad (when)
import Data.Version (showVersion)
import Dhall.Parser (exprFromText)
import Filesystem.Path.CurrentOS (FilePath)
import Options.Generic (Generic, ParseRecord, type (<?>)(..))
import Prelude hiding (FilePath)
import System.IO (stderr)
import System.Exit (exitFailure, exitSuccess)
import Text.Trifecta.Delta (Delta(..))
import qualified Paths_dhall as Meta
import qualified Control.Exception
import qualified Data.Text.IO
import qualified Data.Text.Lazy
import qualified Data.Text.Lazy.IO
import qualified Data.Text.Prettyprint.Doc as Pretty
import qualified Data.Text.Prettyprint.Doc.Render.Text as Pretty
import qualified Filesystem.Path.CurrentOS
import qualified Options.Generic
import qualified System.IO
data Options = Options
{ version :: Bool <?> "Display version and exit"
, inplace :: Maybe FilePath <?> "Modify the specified file in-place"
} deriving (Generic)
instance ParseRecord Options
opts :: Pretty.LayoutOptions
opts =
Pretty.defaultLayoutOptions
{ Pretty.layoutPageWidth = Pretty.AvailablePerLine 80 1.0 }
main :: IO ()
main = do
options <- Options.Generic.getRecord "Formatter for the Dhall language"
when (unHelpful (version options)) $ do
putStrLn (showVersion Meta.version)
exitSuccess
let handler e = do
let _ = e :: SomeException
System.IO.hPrint stderr e
System.Exit.exitFailure
Control.Exception.handle handler (do
case unHelpful (inplace options) of
Just file -> do
let fileString = Filesystem.Path.CurrentOS.encodeString file
strictText <- Data.Text.IO.readFile fileString
let lazyText = Data.Text.Lazy.fromStrict strictText
expr <- case exprFromText (Directed "(stdin)" 0 0 0 0) lazyText of
Left err -> Control.Exception.throwIO err
Right expr -> return expr
let doc = Pretty.pretty expr
System.IO.withFile fileString System.IO.WriteMode (\handle -> do
Pretty.renderIO handle (Pretty.layoutSmart opts doc) )
Nothing -> do
inText <- Data.Text.Lazy.IO.getContents
expr <- case exprFromText (Directed "(stdin)" 0 0 0 0) inText of
Left err -> Control.Exception.throwIO err
Right expr -> return expr
let doc = Pretty.pretty expr
Pretty.renderIO System.IO.stdout (Pretty.layoutSmart opts doc)
Data.Text.IO.putStrLn "" )

View File

@ -101,6 +101,7 @@ Library
http-client-tls >= 0.2.0 && < 0.4 ,
lens >= 2.4 && < 4.16,
parsers >= 0.12.4 && < 0.13,
prettyprinter >= 1.1.1 && < 1.2 ,
system-filepath >= 0.3.1 && < 0.5 ,
system-fileio >= 0.2.1 && < 0.4 ,
text >= 0.11.1.0 && < 1.3 ,
@ -120,7 +121,7 @@ Library
GHC-Options: -Wall
Executable dhall
Hs-Source-Dirs: exec
Hs-Source-Dirs: dhall
Main-Is: Main.hs
Build-Depends:
base >= 4 && < 5 ,
@ -132,6 +133,21 @@ Executable dhall
Other-Modules:
Paths_dhall
Executable dhall-format
Hs-Source-Dirs: dhall-format
Main-Is: Main.hs
Build-Depends:
base >= 4 && < 5 ,
dhall ,
optparse-generic >= 1.1.1 && < 1.3,
prettyprinter >= 1.1.1 && < 1.2,
system-filepath >= 0.3.1 && < 0.5,
trifecta >= 1.6 && < 1.8,
text >= 0.11.1.0 && < 1.3
GHC-Options: -Wall
Other-Modules:
Paths_dhall
Test-Suite test
Type: exitcode-stdio-1.0
Hs-Source-Dirs: tests

View File

@ -27,8 +27,6 @@ import qualified Dhall.TypeCheck
import qualified Options.Generic
import qualified System.IO
data Mode = Default | Resolve | TypeCheck | Normalize
data Options = Options
{ explain :: Bool <?> "Explain error messages in more detail"
, version :: Bool <?> "Display version and exit"

View File

@ -55,6 +55,7 @@ import Data.String (IsString(..))
import Data.Text.Buildable (Buildable(..))
import Data.Text.Lazy (Text)
import Data.Text.Lazy.Builder (Builder)
import Data.Text.Prettyprint.Doc (Doc, Pretty)
import Data.Traversable
import Data.Vector (Vector)
import Filesystem.Path.CurrentOS (FilePath)
@ -63,11 +64,13 @@ import Prelude hiding (FilePath, succ)
import qualified Control.Monad
import qualified Data.HashSet
import qualified Data.List
import qualified Data.Map
import qualified Data.Maybe
import qualified Data.Text
import qualified Data.Text.Lazy as Text
import qualified Data.Text.Lazy.Builder as Builder
import qualified Data.Text.Prettyprint.Doc as Pretty
import qualified Data.Vector
import qualified Data.Vector.Mutable
import qualified Filesystem.Path.CurrentOS as Filesystem
@ -141,6 +144,9 @@ instance Buildable Path where
RawText -> "as Text"
Code -> ""
instance Pretty Path where
pretty path = Pretty.pretty (Builder.toLazyText (build path))
{-| Label for a bound variable
The `Text` field is the variable's name (i.e. \"@x@\").
@ -451,6 +457,558 @@ instance IsString (Expr s a) where
case the corresponding builder.
-}
{-| Internal utility for pretty-printing, used when generating element lists
to supply to `enclose` or `enclose'`. This utility indicates that the
compact represent is the same as the multi-line representation for each
element
-}
duplicate :: a -> (a, a)
duplicate x = (x, x)
-- | Pretty-print a list
list :: [Doc ann] -> Doc ann
list [] = "[]"
list docs = enclose "[ " "[ " ", " ", " " ]" "]" (fmap duplicate docs)
-- | Pretty-print union types and literals
angles :: [(Doc ann, Doc ann)] -> Doc ann
angles [] = "<>"
angles docs = enclose "< " "< " " | " "| " " >" ">" docs
-- | Pretty-print record types and literals
braces :: [(Doc ann, Doc ann)] -> Doc ann
braces [] = "{}"
braces docs = enclose "{ " "{ " ", " ", " " }" "}" docs
-- | Pretty-print anonymous functions and function types
arrows :: [(Doc ann, Doc ann)] -> Doc ann
arrows = enclose' "" " " "" ""
{-| Format an expression that holds a variable number of elements, such as a
list, record, or union
-}
enclose
:: Doc ann
-- ^ Beginning document for compact representation
-> Doc ann
-- ^ Beginning document for multi-line representation
-> Doc ann
-- ^ Separator for compact representation
-> Doc ann
-- ^ Separator for multi-line representation
-> Doc ann
-- ^ Ending document for compact representation
-> Doc ann
-- ^ Ending document for multi-line representation
-> [(Doc ann, Doc ann)]
-- ^ Elements to format, each of which is a pair: @(compact, multi-line)@
-> Doc ann
enclose beginShort _ _ _ endShort _ [] =
beginShort <> endShort
where
enclose beginShort beginLong sepShort sepLong endShort endLong docs =
Pretty.group
(Pretty.flatAlt
(Pretty.align
(mconcat (zipWith combineLong (beginLong : repeat sepLong) docsLong) <> endLong)
)
(mconcat (zipWith combineShort (beginShort : repeat sepShort) docsShort) <> endShort)
)
where
docsShort = fmap fst docs
docsLong = fmap snd docs
combineLong x y = x <> y <> Pretty.hardline
combineShort x y = x <> y
{-| Format an expression that holds a variable number of elements without a
trailing document such as nested `let`, nested lambdas, or nested `forall`s
-}
enclose'
:: Doc ann
-- ^ Beginning document for compact representation
-> Doc ann
-- ^ Beginning document for multi-line representation
-> Doc ann
-- ^ Separator for compact representation
-> Doc ann
-- ^ Separator for multi-line representation
-> [(Doc ann, Doc ann)]
-- ^ Elements to format, each of which is a pair: @(compact, multi-line)@
-> Doc ann
enclose' beginShort beginLong sepShort sepLong docs =
Pretty.group (Pretty.flatAlt long short)
where
longLines = zipWith (<>) (beginLong : repeat sepLong) docsLong
long =
Pretty.align (mconcat (Data.List.intersperse Pretty.hardline longLines))
short = mconcat (zipWith (<>) (beginShort : repeat sepShort) docsShort)
docsShort = fmap fst docs
docsLong = fmap snd docs
prettyLabel :: Text -> Doc ann
prettyLabel a =
if Data.HashSet.member a reservedIdentifiersText
then "`" <> Pretty.pretty a <> "`"
else Pretty.pretty a
prettyNumber :: Integer -> Doc ann
prettyNumber = Pretty.pretty
prettyNatural :: Natural -> Doc ann
prettyNatural = Pretty.pretty
prettyDouble :: Double -> Doc ann
prettyDouble = Pretty.pretty
prettyText :: Builder -> Doc ann
prettyText a = Pretty.pretty (show a)
prettyConst :: Const -> Doc ann
prettyConst Type = "Type"
prettyConst Kind = "Kind"
prettyVar :: Var -> Doc ann
prettyVar (V x 0) = prettyLabel x
prettyVar (V x n) = prettyLabel x <> "@" <> prettyNumber n
prettyExprA :: Pretty a => Expr s a -> Doc ann
prettyExprA a0@(Annot _ _) =
enclose' "" " " " : " ": " (fmap duplicate (docs a0))
where
docs (Annot a b) = prettyExprB a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprB b ]
prettyExprA a0 = prettyExprB a0
prettyExprB :: Pretty a => Expr s a -> Doc ann
prettyExprB a0@(Lam _ _ _) = arrows (fmap duplicate (docs a0))
where
docs (Lam a b c) = Pretty.group (Pretty.flatAlt long short) : docs c
where
long = "λ "
<> Pretty.align
( "( "
<> prettyLabel a
<> Pretty.hardline
<> ": "
<> prettyExprA b
<> Pretty.hardline
<> ")"
)
short = "λ("
<> prettyLabel a
<> " : "
<> prettyExprA b
<> ")"
docs (Note _ c) = docs c
docs c = [ prettyExprB c ]
prettyExprB (BoolIf a b c) =
Pretty.group (Pretty.flatAlt long short)
where
long =
Pretty.align
( "if "
<> prettyExprA a
<> Pretty.hardline
<> "then "
<> prettyExprB b
<> Pretty.hardline
<> "else "
<> prettyExprC c
)
short = "if "
<> prettyExprA a
<> " then "
<> prettyExprB b
<> " else "
<> prettyExprC c
prettyExprB a0@(Pi _ _ _) =
arrows (fmap duplicate (docs a0))
where
docs (Pi "_" b c) = prettyExprC b : docs c
docs (Pi a b c) = Pretty.group (Pretty.flatAlt long short) : docs c
where
long = ""
<> Pretty.align
( "( "
<> prettyLabel a
<> Pretty.hardline
<> ": "
<> prettyExprA b
<> Pretty.hardline
<> ")"
)
short = "∀("
<> prettyLabel a
<> " : "
<> prettyExprA b
<> ")"
docs (Note _ c) = docs c
docs c = [ prettyExprB c ]
prettyExprB a0@(Let _ _ _ _) =
enclose' "" " " " in " (Pretty.hardline <> "in ")
(fmap duplicate (docs a0))
where
docs (Let a Nothing c d) =
Pretty.group (Pretty.flatAlt long short) : docs d
where
long = "let "
<> Pretty.align
( prettyLabel a
<> " ="
<> Pretty.hardline
<> " "
<> prettyExprA c
)
short = "let "
<> prettyLabel a
<> " = "
<> prettyExprA c
docs (Let a (Just b) c d) =
Pretty.group (Pretty.flatAlt long short) : docs d
where
long = "let "
<> Pretty.align
( prettyLabel a
<> Pretty.hardline
<> ": "
<> prettyExprA b
<> Pretty.hardline
<> "= "
<> prettyExprA c
)
short = "let "
<> prettyLabel a
<> " : "
<> prettyExprA b
<> " = "
<> prettyExprA c
docs (Note _ d) =
docs d
docs d =
[ prettyExprB d ]
prettyExprB (ListLit Nothing b) =
list (map prettyExprA (Data.Vector.toList b))
prettyExprB (ListLit (Just a) b) =
list (map prettyExprA (Data.Vector.toList b))
<> " : "
<> prettyExprD (App List a)
prettyExprB (OptionalLit a b) =
list (map prettyExprA (Data.Vector.toList b))
<> " : "
<> prettyExprD (App Optional a)
prettyExprB (Merge a b (Just c)) =
Pretty.group (Pretty.flatAlt long short)
where
long =
Pretty.align
( "merge"
<> Pretty.hardline
<> prettyExprE a
<> Pretty.hardline
<> prettyExprE b
<> Pretty.hardline
<> ": "
<> prettyExprD c
)
short = "merge "
<> prettyExprE a
<> " "
<> prettyExprE b
<> " : "
<> prettyExprD c
prettyExprB (Merge a b Nothing) =
Pretty.group (Pretty.flatAlt long short)
where
long =
Pretty.align
( "merge"
<> Pretty.hardline
<> prettyExprE a
<> Pretty.hardline
<> prettyExprE b
)
short = "merge "
<> prettyExprE a
<> " "
<> prettyExprE b
prettyExprB (Note _ b) =
prettyExprB b
prettyExprB a =
prettyExprC a
prettyExprC :: Pretty a => Expr s a -> Doc ann
prettyExprC = prettyExprC0
prettyExprC0 :: Pretty a => Expr s a -> Doc ann
prettyExprC0 a0@(BoolOr _ _) =
enclose' "" " " " || " "|| " (fmap duplicate (docs a0))
where
docs (BoolOr a b) = prettyExprC1 a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprC1 b ]
prettyExprC0 a0 =
prettyExprC1 a0
prettyExprC1 :: Pretty a => Expr s a -> Doc ann
prettyExprC1 a0@(TextAppend _ _) =
enclose' "" " " " ++ " "++ " (fmap duplicate (docs a0))
where
docs (TextAppend a b) = prettyExprC2 a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprC2 b ]
prettyExprC1 a0 =
prettyExprC2 a0
prettyExprC2 :: Pretty a => Expr s a -> Doc ann
prettyExprC2 a0@(NaturalPlus _ _) =
enclose' "" " " " + " "+ " (fmap duplicate (docs a0))
where
docs (NaturalPlus a b) = prettyExprC3 a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprC3 b ]
prettyExprC2 a0 =
prettyExprC3 a0
prettyExprC3 :: Pretty a => Expr s a -> Doc ann
prettyExprC3 a0@(ListAppend _ _) =
enclose' "" " " " # " "# " (fmap duplicate (docs a0))
where
docs (ListAppend a b) = prettyExprC4 a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprC4 b ]
prettyExprC3 a0 =
prettyExprC4 a0
prettyExprC4 :: Pretty a => Expr s a -> Doc ann
prettyExprC4 a0@(BoolAnd _ _) =
enclose' "" " " " && " "&& " (fmap duplicate (docs a0))
where
docs (BoolAnd a b) = prettyExprC5 a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprC5 b ]
prettyExprC4 a0 =
prettyExprC5 a0
prettyExprC5 :: Pretty a => Expr s a -> Doc ann
prettyExprC5 a0@(Combine _ _) =
enclose' "" " " "" "" (fmap duplicate (docs a0))
where
docs (Combine a b) = prettyExprC6 a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprC6 b ]
prettyExprC5 a0 =
prettyExprC6 a0
prettyExprC6 :: Pretty a => Expr s a -> Doc ann
prettyExprC6 a0@(Prefer _ _) =
enclose' "" " " "" "" (fmap duplicate (docs a0))
where
docs (Prefer a b) = prettyExprC7 a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprC7 b ]
prettyExprC6 a0 =
prettyExprC7 a0
prettyExprC7 :: Pretty a => Expr s a -> Doc ann
prettyExprC7 a0@(NaturalTimes _ _) =
enclose' "" " " " * " "* " (fmap duplicate (docs a0))
where
docs (NaturalTimes a b) = prettyExprC8 a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprC8 b ]
prettyExprC7 a0 =
prettyExprC8 a0
prettyExprC8 :: Pretty a => Expr s a -> Doc ann
prettyExprC8 a0@(BoolEQ _ _) =
enclose' "" " " " == " "== " (fmap duplicate (docs a0))
where
docs (BoolEQ a b) = prettyExprC9 a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprC9 b ]
prettyExprC8 a0 =
prettyExprC9 a0
prettyExprC9 :: Pretty a => Expr s a -> Doc ann
prettyExprC9 a0@(BoolNE _ _) =
enclose' "" " " " != " "!= " (fmap duplicate (docs a0))
where
docs (BoolNE a b) = prettyExprD a : docs b
docs (Note _ b) = docs b
docs b = [ prettyExprD b ]
prettyExprC9 a0 =
prettyExprD a0
prettyExprD :: Pretty a => Expr s a -> Doc ann
prettyExprD a0@(App _ _) =
enclose' "" "" " " "" (fmap duplicate (reverse (docs a0)))
where
docs (App a b) = prettyExprE b : docs a
docs (Note _ b) = docs b
docs b = [ prettyExprE b ]
prettyExprD (Note _ b) = prettyExprD b
prettyExprD a0 =
prettyExprE a0
prettyExprE :: Pretty a => Expr s a -> Doc ann
prettyExprE (Field a b) = prettyExprE a <> "." <> prettyLabel b
prettyExprE (Note _ b) = prettyExprE b
prettyExprE a = prettyExprF a
prettyExprF :: Pretty a => Expr s a -> Doc ann
prettyExprF (Var a) =
prettyVar a
prettyExprF (Const k) =
prettyConst k
prettyExprF Bool =
"Bool"
prettyExprF Natural =
"Natural"
prettyExprF NaturalFold =
"Natural/fold"
prettyExprF NaturalBuild =
"Natural/build"
prettyExprF NaturalIsZero =
"Natural/isZero"
prettyExprF NaturalEven =
"Natural/even"
prettyExprF NaturalOdd =
"Natural/odd"
prettyExprF NaturalToInteger =
"Natural/toInteger"
prettyExprF NaturalShow =
"Natural/show"
prettyExprF Integer =
"Integer"
prettyExprF IntegerShow =
"Integer/show"
prettyExprF Double =
"Double"
prettyExprF DoubleShow =
"Double/show"
prettyExprF Text =
"Text"
prettyExprF List =
"List"
prettyExprF ListBuild =
"List/build"
prettyExprF ListFold =
"List/fold"
prettyExprF ListLength =
"List/length"
prettyExprF ListHead =
"List/head"
prettyExprF ListLast =
"List/last"
prettyExprF ListIndexed =
"List/indexed"
prettyExprF ListReverse =
"List/reverse"
prettyExprF Optional =
"Optional"
prettyExprF OptionalFold =
"Optional/fold"
prettyExprF OptionalBuild =
"Optional/build"
prettyExprF (BoolLit True) =
"True"
prettyExprF (BoolLit False) =
"False"
prettyExprF (IntegerLit a) =
prettyNumber a
prettyExprF (NaturalLit a) =
"+" <> prettyNatural a
prettyExprF (DoubleLit a) =
prettyDouble a
prettyExprF (TextLit a) =
prettyText a
prettyExprF (Record a) =
prettyRecord a
prettyExprF (RecordLit a) =
prettyRecordLit a
prettyExprF (Union a) =
prettyUnion a
prettyExprF (UnionLit a b c) =
prettyUnionLit a b c
prettyExprF (ListLit Nothing b) =
list (map prettyExprA (Data.Vector.toList b))
prettyExprF (Embed a) =
Pretty.pretty a
prettyExprF (Note _ b) =
prettyExprF b
prettyExprF a =
Pretty.group (Pretty.flatAlt long short)
where
long = Pretty.align ("( " <> prettyExprA a <> Pretty.hardline <> ")")
short = "(" <> prettyExprA a <> ")"
prettyKeyValue
:: Pretty a
=> Doc ann
-> Int
-> (Text, Expr s a)
-> (Doc ann, Doc ann)
prettyKeyValue separator keyLength (key, value) =
( prettyLabel key <> " " <> separator <> " " <> prettyExprA value
, Pretty.fill keyLength (prettyLabel key)
<> " "
<> separator
<> Pretty.group (Pretty.flatAlt long short)
)
where
long = Pretty.hardline <> " " <> prettyExprA value
short = " " <> prettyExprA value
prettyRecord :: Pretty a => Map Text (Expr s a) -> Doc ann
prettyRecord a = braces (map adapt (Data.Map.toList a))
where
keyLength = fromIntegral (maximum (map Text.length (Data.Map.keys a)))
adapt = prettyKeyValue ":" keyLength
prettyRecordLit :: Pretty a => Map Text (Expr s a) -> Doc ann
prettyRecordLit a
| Data.Map.null a = "{=}"
| otherwise = braces (map adapt (Data.Map.toList a))
where
keyLength = fromIntegral (maximum (map Text.length (Data.Map.keys a)))
adapt = prettyKeyValue "=" keyLength
prettyUnion :: Pretty a => Map Text (Expr s a) -> Doc ann
prettyUnion a = angles (map adapt (Data.Map.toList a))
where
keyLength = fromIntegral (maximum (map Text.length (Data.Map.keys a)))
adapt = prettyKeyValue ":" keyLength
prettyUnionLit :: Pretty a => Text -> Expr s a -> Map Text (Expr s a) -> Doc ann
prettyUnionLit a b c = angles (front : map adapt (Data.Map.toList c))
where
keyLength = fromIntegral (maximum (map Text.length (a : Data.Map.keys c)))
front = prettyKeyValue "=" keyLength (a, b)
adapt = prettyKeyValue ":" keyLength
-- | Pretty-print a value
pretty :: Buildable a => a -> Text
pretty = Builder.toLazyText . build
@ -797,10 +1355,12 @@ buildUnionLit a b c
<> " >"
-- | Generates a syntactically valid Dhall program
instance Buildable a => Buildable (Expr s a)
where
instance Buildable a => Buildable (Expr s a) where
build = buildExpr
instance Pretty a => Pretty (Expr s a) where
pretty = prettyExprA
{-| `shift` is used by both normalization and type-checking to avoid variable
capture by shifting variable indices

View File

@ -50,6 +50,9 @@ module Dhall.Tutorial (
-- * Headers
-- $headers
-- * Formatting code
-- $format
-- * Built-in functions
-- $builtins
@ -1381,6 +1384,173 @@ import Dhall
-- ... and @http:\/\/example.com@ contains a relative import of @./foo@ then
-- Dhall will import @http:\/\/example.com/foo@ using the same @./headers@ file.
-- $format
--
-- This package also provides a @dhall-format@ executable that you can use to
-- automatically format Dhall expressions. For example, we can take the
-- following unformatted Dhall expression:
--
-- > $ cat ./unformatted
-- > λ(a : Type) → λ(kvss : List (List { index : Natural, value : a })) →
-- > List/build { index : Natural, value : a } (λ(list : Type) → λ(cons : {
-- > index : Natural, value : a } → list → list) → λ(nil : list) →
-- > (List/fold (List { index : Natural, value : a }) kvss { count : Natural, diff :
-- > Natural → list } (λ(kvs : List { index : Natural, value : a }) → λ(y : {
-- > count : Natural, diff : Natural → list }) → { count = y.count + List/length
-- > { index : Natural, value : a } kvs, diff = λ(n : Natural) → List/fold {
-- > index : Natural, value : a } kvs list (λ(kvOld : { index : Natural, value : a
-- > }) → λ(z : list) → cons { index = kvOld.index + n, value = kvOld.value }
-- > z) (y.diff (n + List/length { index : Natural, value : a } kvs)) }) { count =
-- > +0, diff = λ(_ : Natural) → nil }).diff +0)
--
-- ... and run the expression through the @dhall-format@ executable:
--
-- > $ dhall-format < ./unformatted
-- > λ(a : Type)
-- > → λ(kvss : List (List { index : Natural, value : a }))
-- > → List/build
-- > { index : Natural, value : a }
-- > ( λ(list : Type)
-- > → λ(cons : { index : Natural, value : a } → list → list)
-- > → λ(nil : list)
-- > → ( List/fold
-- > (List { index : Natural, value : a })
-- > kvss
-- > { count : Natural, diff : Natural → list }
-- > ( λ(kvs : List { index : Natural, value : a })
-- > → λ(y : { count : Natural, diff : Natural → list })
-- > → { count = y.count + List/length { index : Natural, value : a } kvs
-- > , diff =
-- > λ(n : Natural)
-- > → List/fold
-- > { index : Natural, value : a }
-- > kvs
-- > list
-- > ( λ(kvOld : { index : Natural, value : a })
-- > → λ(z : list)
-- > → cons { index = kvOld.index + n, value = kvOld.value } z
-- > )
-- > (y.diff (n + List/length { index : Natural, value : a } kvs))
-- > }
-- > )
-- > { count = +0, diff = λ(_ : Natural) → nil }
-- > ).diff
-- > +0
-- > )
--
-- The executable formats expressions without resolving, type-checking, or
-- normalizing them:
--
-- > $ dhall-format
-- > let replicate = https://ipfs.io/ipfs/QmQ8w5PLcsNz56dMvRtq54vbuPe9cNnCCUXAQp6xLc6Ccx/Prelude/List/replicate
-- > in replicate +5 (List (List Integer)) (replicate +5 (List Integer) (replicate +5 Integer 1))
-- > <Ctrl-D>
-- > let replicate =
-- > https://ipfs.io/ipfs/QmQ8w5PLcsNz56dMvRtq54vbuPe9cNnCCUXAQp6xLc6Ccx/Prelude/List/replicate
-- >
-- > in replicate
-- > +5
-- > (List (List Integer))
-- > (replicate +5 (List Integer) (replicate +5 Integer 1))
--
-- If you want to evaluate and format an expression then you can combine the
-- @dhall@ and @dhall-format@ executables:
--
-- > $ dhall | dhall-format
-- > let replicate = https://ipfs.io/ipfs/QmQ8w5PLcsNz56dMvRtq54vbuPe9cNnCCUXAQp6xLc6Ccx/Prelude/List/replicate
-- > in replicate +5 (List (List Integer)) (replicate +5 (List Integer) (replicate +5 Integer 1))
-- > <Ctrl-D>
-- > List (List (List Integer))
-- >
-- > ( [ ( [ ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > ]
-- > : List (List Integer)
-- > )
-- > , ( [ ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > ]
-- > : List (List Integer)
-- > )
-- > , ( [ ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > ]
-- > : List (List Integer)
-- > )
-- > , ( [ ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > ]
-- > : List (List Integer)
-- > )
-- > , ( [ ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > , ([ 1, 1, 1, 1, 1 ] : List Integer)
-- > ]
-- > : List (List Integer)
-- > )
-- > ]
-- > : List (List (List Integer))
-- > )
--
-- You can also use the formatter to modify files in place using the
-- @--inplace@ flag:
--
-- > $ dhall-format --inplace ./unformatted
-- > $ cat ./unformatted
-- > λ(a : Type)
-- > → λ(kvss : List (List { index : Natural, value : a }))
-- > → List/build
-- > { index : Natural, value : a }
-- > ( λ(list : Type)
-- > → λ(cons : { index : Natural, value : a } → list → list)
-- > → λ(nil : list)
-- > → ( List/fold
-- > (List { index : Natural, value : a })
-- > kvss
-- > { count : Natural, diff : Natural → list }
-- > ( λ(kvs : List { index : Natural, value : a })
-- > → λ(y : { count : Natural, diff : Natural → list })
-- > → { count = y.count + List/length { index : Natural, value : a } kvs
-- > , diff =
-- > λ(n : Natural)
-- > → List/fold
-- > { index : Natural, value : a }
-- > kvs
-- > list
-- > ( λ(kvOld : { index : Natural, value : a })
-- > → λ(z : list)
-- > → cons { index = kvOld.index + n, value = kvOld.value } z
-- > )
-- > (y.diff (n + List/length { index : Natural, value : a } kvs))
-- > }
-- > )
-- > { count = +0, diff = λ(_ : Natural) → nil }
-- > ).diff
-- > +0
-- > )
--
-- Carefully note that the code formatter does not yet preserve comments:
--
-- > $ dhall-format
-- > -- Hello!
-- > 1
-- > <Ctrl-D>
-- > 1
-- $builtins
--
-- Dhall is a restricted programming language that only supports simple built-in

View File

@ -25,6 +25,7 @@ import Data.Set (Set)
import Data.Text.Buildable (Buildable(..))
import Data.Text.Lazy (Text)
import Data.Text.Lazy.Builder (Builder)
import Data.Text.Prettyprint.Doc (Pretty(..))
import Data.Traversable (forM)
import Data.Typeable (Typeable)
import Dhall.Core (Const(..), Expr(..), Var(..))
@ -654,6 +655,9 @@ instance Eq X where
instance Buildable X where
build = absurd
instance Pretty X where
pretty = absurd
-- | The specific type error
data TypeMessage s
= UnboundVariable Text