packages feed

ghc-source-gen (empty) → 0.1.0.0

raw patch · 25 files changed

+2404/−0 lines, 25 filesdep +basedep +ghcdep +ghc-pathssetup-changed

Dependencies added: base, ghc, ghc-paths, ghc-source-gen, tasty, tasty-hunit

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for haskell-syntax++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,28 @@+Copyright 2019 Google LLC++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 Google LLC nor the names of its+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.
+ README.md view
@@ -0,0 +1,150 @@+# ghc-source-gen++`ghc-source-gen` is a Haskell library for generating Haskell source files and+code fragments.  It uses GHC's [library API] for the latest up-to-date syntax, and+provides a simple, consistent interface across several major versions of GHC.++To get started, take a look at the [example](#example) below, or check out the+`GHC.SourceGen` module.++This package is not an officially supported Google product.++[library API]: https://hackage.haskell.org/package/ghc++## Example++The following example creates a module that defines the+`const` function:++```haskell+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+import GHC.SourceGen+import GHC.Paths (libdir)++constModule :: HsModule'+constModule =+    module' (Just "Const") (Just [var "const"]) []+        [ typeSig "const" $ a --> b --> a+        , funBind "const" $ matchRhs [wildP, x] x+        ]+  where+    a = var "a"+    b = var "b"+    x = var "x"++main = runGhc (Just libdir) $ putPpr constModule+```++The output of that program is:++```+module Const (+        const+    ) where+const :: a -> b -> a+const _ x = x+```++## Comparison with the GHC API++The raw GHC API has several complexities that `ghc-source-gen` simplifies for the+purpose of source code generation.++### Backwards-compatibility+`ghc-source-gen` provides the same API across several versions of GHC.  Code written+with `ghc-source-gen` should compile unchanged on each of those versions.++Currently, this library supports GHC versions 8.2, 8.4, 8.6 and 8.8.++One caveat: in the future, `ghc-source-gen` will support some forms of syntax+which are not implemented by all of those GHC versions.  For example, the+`DerivingVia` extension is only implemented in `ghc >= 8.6`.  When built on+older versions of GHC, `ghc-source-gen` will omit functions for constructing+that syntax.  We will also tag any such function with a note in its Haddock+documentation.++### Less verbose types and construction functions++The datatypes that GHC uses to represent Haskell syntax change their+representation at different stages of the compilation: for example, parsing,+renaming, or type-checking.  That data transformation provides type safety and+a uniform structure across the phases.  However, it also adds unnecessary+complexity to the task of source code generation.++`ghc-source-gen` aims to provide a simple interface by creating data types as+GHC would represent them immediately after its *parsing* step.  For example,+`ghc >= 8.4` uses a type parameter `p` in its syntax types: `HsExpr p` for+expressions, `HsDecl p` for declarations, etc.  `ghc-source-gen` defines type+synonyms for them:++```haskell+type HsExpr' = HsExpr GhcPs+type HsDecl' = HsDecl GhcPs+type HsType' = HsType GhcPs+-- etc.+```++Furthermore, most constructors take an extra "extension" field which can+contain different information in different stages, influenced by the parameter+`p`.  In almost all cases, after the parsing step that field is the+trivial type `data NoExt = NoExt`.  (For more details, see the [Trees that+Grow] paper. GHC versions earlier than 8.4 used a similar `PlaceHolder` type.).+This extra data makes code generation more verbose.++`ghc-source-gen` automatically sets the `NoExt` value (or equivalent) for the+terms that it generates, hiding that detail from its external API.  It also+sets and hides other fields that are irrelevant to parsing or pretty-printing,+such as simplifier ticks.++[Trees that Grow]: https://gitlab.haskell.org/ghc/ghc/wikis/implementing-trees-that-grow++### Source Locations+GHC carefully tracks the source location of (nearly) every node in the AST.+That information is very useful for error reporting.  However, it would be too+verbose to set it explicitly for each individual node during code generation.+Furthermore, GHC doesn't use the source location when pretty-printing its+output, which is `ghc-source-gen`'s main use case.++Currently, `ghc-source-gen` gives to each node it generates a trivial location+without an explicit line or column.++### Parentheses+GHC represents parentheses explicitly in its syntax tree, so that it can print code+exactly as it was parsed.  Unfortunately, this means that its pretty-printing code+expects those parentheses to be present, and outputs incorrect+source code if they are missing.  `ghc-source-gen`+adds parentheses automatically in the code that it generates.++For example, consider a simplified expression syntax:++```haskell+data Expr+    = VarE String      -- ^ Variables+    | App Expr Expr    -- ^ Function application+    | Paren Expr Expr  -- ^ Parentheses+```++Then GHC would pretty-print as `"f (g x)"` the tree++```haskell+App (VarE "f") $ Paren $ App (VarE "g") (VarE "h")+```++But without the explicit parenthesis, it would pretty-print as `"f g x"`:++```haskell+App (VarE "f") $ App (VarE "g") (VarE "h")+```++which misrepresents the precedence between the two function applications.++`ghc-source-gen` resolves this issue by inserting parentheses automatically, and only when necessary.  In the expression++```haskell+var "f" @@ (var "g" @@ var "h")+```++it inserts a parenthesis automatically so that the result pretty-prints to `"f (g x)"` as expected.++GHC uses a similar approach internally itself.  For more discussion, see tickets [14289](https://gitlab.haskell.org/ghc/ghc/issues/14289) and [15738](https://gitlab.haskell.org/ghc/ghc/issues/15738).
+ Setup.hs view
@@ -0,0 +1,8 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++import Distribution.Simple+main = defaultMain
+ ghc-source-gen.cabal view
@@ -0,0 +1,95 @@+-- This file has been generated from package.yaml by hpack version 0.28.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: bbf81e9b932cf501a87fab8a5ebcea3df292f1cab234cad74fb08185047e9388++name:           ghc-source-gen+version:        0.1.0.0+synopsis:       Constructs Haskell syntax trees for the GHC API.+description:    @ghc-source-gen@ is a library for generating Haskell source code.+                It uses the <https://hackage.haskell.org/package/ghc ghc> library+                to support recent language extensions, and provides a simple, consistent+                interface across several major versions of GHC.+                .+                To get started, take a look at the "GHC.SourceGen" module.+                .+                For more information, please see the <https://github.com/google/ghc-source-gen README>.+category:       Development+homepage:       https://github.com/google/ghc-source-gen#readme+bug-reports:    https://github.com/google/ghc-source-gen/issues+author:         Judah Jacobson+maintainer:     judahjacobson@google.com+copyright:      Google LLC+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10+extra-source-files:+    ChangeLog.md+    README.md++source-repository head+  type: git+  location: https://github.com/google/ghc-source-gen++library+  exposed-modules:+      GHC.SourceGen+      GHC.SourceGen.Binds+      GHC.SourceGen.Decl+      GHC.SourceGen.Expr+      GHC.SourceGen.Lit+      GHC.SourceGen.Module+      GHC.SourceGen.Name+      GHC.SourceGen.Overloaded+      GHC.SourceGen.Pat+      GHC.SourceGen.Pretty+      GHC.SourceGen.Syntax+      GHC.SourceGen.Type+  other-modules:+      GHC.SourceGen.Binds.Internal+      GHC.SourceGen.Expr.Internal+      GHC.SourceGen.Lit.Internal+      GHC.SourceGen.Name.Internal+      GHC.SourceGen.Syntax.Internal+      GHC.SourceGen.Type.Internal+  hs-source-dirs:+      src+  default-extensions: DataKinds FlexibleInstances TypeSynonymInstances+  build-depends:+      base >=4.7 && <5+    , ghc >=8.2 && <8.9+  default-language: Haskell2010++test-suite pprint_examples+  type: exitcode-stdio-1.0+  main-is: pprint_examples.hs+  other-modules:+      Paths_ghc_source_gen+  hs-source-dirs:+      tests+  default-extensions: DataKinds FlexibleInstances TypeSynonymInstances+  build-depends:+      base >=4.7 && <5+    , ghc >=8.2 && <8.9+    , ghc-paths ==0.1.*+    , ghc-source-gen+  default-language: Haskell2010++test-suite pprint_test+  type: exitcode-stdio-1.0+  main-is: pprint_test.hs+  other-modules:+      Paths_ghc_source_gen+  hs-source-dirs:+      tests+  default-extensions: DataKinds FlexibleInstances TypeSynonymInstances+  build-depends:+      base >=4.7 && <5+    , ghc >=8.2 && <8.9+    , ghc-paths ==0.1.*+    , ghc-source-gen+    , tasty+    , tasty-hunit+  default-language: Haskell2010
+ src/GHC/SourceGen.hs view
@@ -0,0 +1,45 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++-- | This module exports all of the definitions in this package in one+-- convenient location.+--+-- For more details and examples of usage, see+-- <https://github.com/google/ghc-source-gen>.+module GHC.SourceGen+    ( -- * Syntax types+      -- | These modules declare combinators for constructing different parts+      -- of a GHC syntax tree.+      module GHC.SourceGen.Syntax,+      module GHC.SourceGen.Name,+      module GHC.SourceGen.Decl,+      module GHC.SourceGen.Expr,+      module GHC.SourceGen.Module,+      module GHC.SourceGen.Pat,+      module GHC.SourceGen.Type,+      -- * Overloaded combinators+      -- | Certain concepts make sense in different+      -- parts of Haskell syntax.  For example, 'var' may be used in+      -- expressions, types, patterns, and import or export lists.+      module GHC.SourceGen.Binds,+      module GHC.SourceGen.Lit,+      module GHC.SourceGen.Overloaded,+      -- * Renders Haskell syntax into text+      module GHC.SourceGen.Pretty,+    ) where++import GHC.SourceGen.Binds+import GHC.SourceGen.Decl+import GHC.SourceGen.Expr+import GHC.SourceGen.Lit+import GHC.SourceGen.Module+import GHC.SourceGen.Name+import GHC.SourceGen.Overloaded+import GHC.SourceGen.Pat+import GHC.SourceGen.Pretty+import GHC.SourceGen.Syntax+import GHC.SourceGen.Type+
+ src/GHC/SourceGen/Binds.hs view
@@ -0,0 +1,223 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++-- | This module provides combinators for constructing Haskell declarations.+module GHC.SourceGen.Binds+    ( -- * Overloaded constructors+      HasValBind(..)+    , typeSig+    , typeSigs+    , funBind+    , funBinds+    -- * Matches+    -- $rawMatch+    , RawMatch+    , match+    , matchRhs+    -- * Right-hand sides+    , RawGRHSs+    , rhs+    -- ** Guards+    , guardedRhs+    , GuardedExpr+    , guards+    , guard+    -- ** Where clauses+    , where'+    , RawValBind+    -- * Statements+    , stmt+    , (<--)+    ) where++import BasicTypes (LexicalFixity(..))+import HsBinds+import HsExpr+import HsDecls+import HsTypes+import TcEvidence (HsWrapper(WpHole))++import GHC.SourceGen.Binds.Internal+import GHC.SourceGen.Name.Internal+import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal+import GHC.SourceGen.Type.Internal (sigWcType)++-- | Declare that a multiple functions or values have a type:+--+-- > f, g :: A+-- > =====+-- > typeSigs ["f", "g"] (var "A")+typeSigs :: HasValBind t => [RdrNameStr] -> HsType' -> t+typeSigs names t =+    sigB $ noExt TypeSig (map typeRdrName names)+        $ sigWcType t++-- | Declare that a function or value has a type:+--+-- > f :: A+-- > =====+-- > typeSig "f" (var "A")+typeSig :: HasValBind t => RdrNameStr -> HsType' -> t+typeSig n = typeSigs [n]++-- | Define a function or value.+--+-- > f = x+-- > =====+-- > funBinds "f" [matchRhs [] "x"]+--+-- > id x = x+-- > =====+-- > funBinds "id" [matchRhs [var "x"] (var "x")]+--+-- > not True = False+-- > not False = True+-- > =====+-- > funBinds "not"+-- >   [ matchRhs [conP "True" []] (var "False")+-- >   , matchRhs [conP "False" []] (var "True")+-- >   ]+funBinds :: HasValBind t => RdrNameStr -> [RawMatch] -> t+funBinds name matches = bindB $ withPlaceHolder+        (noExt FunBind name'+            (matchGroup context matches) WpHole)+        []+  where+    name' = valueRdrName name+    context = FunRhs name' Prefix NoSrcStrict++-- | Defines a function that has a single case.+--+-- > f = x+-- > =====+-- > funBind "f" (matchRhs [] "x")+--+-- > id x = x+-- > =====+-- > funBind "id" $ matchRhs [var "x"] (var "x")+--+funBind :: HasValBind t => RdrNameStr -> RawMatch -> t+funBind name m = funBinds name [m]++{- $rawMatch++A function definition is made up of one or more 'RawMatch' terms.  Each+'RawMatch' corresponds to a single pattern match.  For example, to define the+"not" function:++> not True = False+> not False = True++We could using a list of two 'RawMatch'es:++> funBinds "not"+>   [ matchRhs [conP "True" []] (var "False")+>   , matchRhs [conP "False" [] (var "True")+>   ]++A match may consist of one or more guarded expressions.  For example, to+define the function as:++> not x+>   | x = False+>   | otherwise = True++We would say:++> funBind "not"+>      $ match [var "x"] $ guardedRhs+>          [ guard (var "x") (var "False")+>          , guard (var "otherwise") (var "True")+>          ]+-}++-- | A function match consisting of multiple guards.+match :: [Pat'] -> RawGRHSs -> RawMatch+match = RawMatch++-- | A function match with a single case.+matchRhs :: [Pat'] -> HsExpr' -> RawMatch+matchRhs ps = match ps . rhs++-- | Adds a "where" clause to an existing 'RawGRHSs'.+--+-- > f x = y+-- >   where y = x+-- > =====+-- > funBind "x"+-- >   $ match [var "x"]+-- >   $ rhs (var "y")+-- >      `where` [patBind (var "y") $ rhs $ var "x']+where' :: RawGRHSs -> [RawValBind] -> RawGRHSs+where' r vbs = r { rawGRHSWhere = rawGRHSWhere r ++ vbs }++-- | A right-hand side of a match, with no guards.+rhs :: HsExpr' -> RawGRHSs+rhs e = guardedRhs [guards [] e]++-- | A guarded right-hand side of a match.+--+-- >   | x = False+-- >   | otherwise = True+-- > =====+-- > guardedRhs+-- >   [ guard (var "x") (var "False")+-- >   , guard (var "otherwise") (var "True")+-- >   ]+guardedRhs :: [GuardedExpr] -> RawGRHSs+guardedRhs ss = RawGRHSs ss []++-- | An expression guarded by a single boolean statement.+--+-- >   | otherwise = ()+-- > =====+-- > guard (var "otherwise") unit+guard :: HsExpr' -> HsExpr' -> GuardedExpr+guard s = guards [stmt s]++-- | An expression guarded by multiple statements, using the @PatternGuards@ extension.+--+-- >   | Just y <- x, y = ()+-- > =====+-- > guards [conP "Just" (var "x") <-- var "y", var "x"] unit+guards :: [Stmt'] -> HsExpr' -> GuardedExpr+guards stmts e = noExt GRHS (map builtLoc stmts) (builtLoc e)++-- | An expression statement.  May be used in a do expression (with 'do'') or in a+-- match (with 'guard').+--+-- TODO: also allow using statements in list comprehensions.+stmt :: HsExpr' -> Stmt'+-- For now, don't worry about rebindable syntax.+stmt e =+    withPlaceHolder $ noExt BodyStmt (builtLoc e) noSyntaxExpr noSyntaxExpr++-- | A statement that binds a pattern.+--+-- > x <- act+-- > =====+-- > var "x" <-- var "act"+(<--) :: Pat' -> HsExpr' -> Stmt'+p <-- e = withPlaceHolder $ noExt BindStmt (builtPat p) (builtLoc e) noSyntaxExpr noSyntaxExpr+infixl 1 <--++-- | Syntax types which can declare/define functions.  For example:+-- declarations, or the body of a class declaration or class instance.+--+-- Use 'typeSig' or 'typeSigs' to declare that functions or values have+-- types, and use 'funBind' to give them definitions.+class HasValBind t where+    sigB :: Sig' -> t+    bindB :: HsBind' -> t++instance HasValBind RawValBind where+    sigB = SigV+    bindB = BindV++instance HasValBind HsDecl' where+    sigB = noExt SigD+    bindB = noExt ValD
+ src/GHC/SourceGen/Binds/Internal.hs view
@@ -0,0 +1,103 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++{-# LANGUAGE CPP #-}+module GHC.SourceGen.Binds.Internal where++import BasicTypes (Origin(Generated))+import Bag (listToBag)+import HsBinds+import HsExpr (MatchGroup(..), Match(..), GRHSs(..))+import SrcLoc (Located)++#if !MIN_VERSION_ghc(8,6,0)+import PlaceHolder (PlaceHolder(..))+#endif+++import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal++data RawValBind+    = SigV Sig'+    | BindV HsBind'++valBinds :: [RawValBind] -> HsLocalBinds'+-- This case prevents GHC from printing an empty "where" clause:+valBinds [] = noExt EmptyLocalBinds+valBinds vbs =+    noExt HsValBinds+#if MIN_VERSION_ghc(8,6,0)+        $ noExt ValBinds+#else+        $ noExt ValBindsIn+#endif+            (listToBag $ map builtLoc binds)+            (map builtLoc sigs)+  where+    sigs = [s | SigV s <- vbs]+    binds = [b | BindV b <- vbs]++-- | A single function pattern match, including an optional "where" clause.+--+-- For example:+--+-- > f x+-- >    | cond = y+-- >    | otherwise = z+-- >  where+-- >    y = ...+-- >    z = ...+data RawMatch = RawMatch+    { rawMatchPats :: [Pat']+    , rawMatchGRHSs :: RawGRHSs+    }++-- | A set of match guards plus an optional "where" clause.+--+-- This type is used in matches and in multi-way if expressions.+--+-- For example:+--+-- >    | cond = y+-- >    | otherwise = z+-- >  where+-- >    y = ...+-- >    z = ...+data RawGRHSs = RawGRHSs+    { rawGRHSs :: [GuardedExpr]+    , rawGRHSWhere :: [RawValBind]+    }++matchGroup :: HsMatchContext' -> [RawMatch] -> MatchGroup' (Located HsExpr')+matchGroup context matches =+    noExt MG (builtLoc $ map (builtLoc . mkMatch) matches)+#if !MIN_VERSION_ghc(8,6,0)+                            [] PlaceHolder+#endif+                            Generated+  where+    mkMatch :: RawMatch -> Match' (Located HsExpr')+    mkMatch r = noExt Match context (map builtPat $ rawMatchPats r)+#if !MIN_VERSION_ghc(8,4,0)+                    -- The GHC docs say: "A type signature for the result of the match."+                    -- The parsing step produces 'Nothing' for this field.+                    Nothing+#endif+                    (mkGRHSs $ rawMatchGRHSs r)++mkGRHSs :: RawGRHSs -> GRHSs' (Located HsExpr')+mkGRHSs g = noExt GRHSs+                (map builtLoc $ rawGRHSs g)+                (builtLoc $ valBinds $ rawGRHSWhere g)++-- | An expression with a single guard.+--+-- For example:+--+-- > | otherwise = ()+type GuardedExpr = GRHS' (Located HsExpr')+
+ src/GHC/SourceGen/Decl.hs view
@@ -0,0 +1,338 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++{-# LANGUAGE CPP #-}+-- | This module provides combinators for constructing Haskell declarations.+module GHC.SourceGen.Decl+    ( -- * Type declarations+      type'+    , newtype'+    , data'+      -- * Pattern bindings+    , patBind+      -- * Data constructors+    , prefixCon+    , infixCon+    , recordCon+    , Field+    , field+    , strict+    , lazy+      -- * Class declarations+    , class'+    , ClassDecl+    , funDep+      -- * Instance declarations+    , instance'+    , RawInstDecl+    ) where++import BasicTypes (LexicalFixity(Prefix))+import Bag (listToBag)+import HsBinds (HsBindLR(..))+import HsDecls+import HsTypes+    ( ConDeclField(..)+    , FieldOcc(..)+    , HsConDetails(..)+    , HsSrcBang(..)+    , HsType(..)+    , SrcStrictness(..)+    , SrcUnpackedness(..)+    )+import SrcLoc (Located)++#if MIN_VERSION_ghc(8,6,0)+import HsExtension (NoExt(NoExt))+#else+import PlaceHolder (PlaceHolder(..))+#endif++import GHC.SourceGen.Binds+import GHC.SourceGen.Binds.Internal (mkGRHSs)+import GHC.SourceGen.Lit.Internal (noSourceText)+import GHC.SourceGen.Name.Internal+import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal+import GHC.SourceGen.Type.Internal++-- | A definition that can appear in the body of a @class@ declaration.+data ClassDecl+    = ClassSig Sig'+    | ClassDefaultMethod HsBind'+    | ClassFunDep [RdrNameStr] [RdrNameStr]+    -- TODO: type families++instance HasValBind ClassDecl where+    sigB = ClassSig+    bindB = ClassDefaultMethod++-- | A functional dependency for a class.+--+-- > | a, b -> c+-- > =====+-- > funDep ["a", "b"] ["c"]+--+-- > class Ident a b | a -> b, b -> a where+-- >   ident :: a -> b+-- > =====+-- > class' [] "Ident" ["a", "b"]+-- >    [ funDep ["a"] ["b"]+-- >    , funDep ["b"] ["a"]+-- >    , typeSig "ident" $ var "a" --> var "b"+-- >    ]+funDep :: [RdrNameStr] -> [RdrNameStr] -> ClassDecl+funDep = ClassFunDep++-- TODO:+-- - kinded variables+-- - fixity of declaration+-- - functional dependencies+-- - associated types++-- | A class declaration.+--+-- > class (Real a, Enum a) => Integral a where+-- >   divMod :: a -> a -> (a, a)+-- >   div :: a -> a -> a+-- >   div x y = fst (divMod x y)+-- > =====+-- > let a = var "a"+-- > in class'+-- >      [var "Real" @@ a, var "Enum" @@ a]+-- >      "Integral"+-- >      ["a"]+-- >      [ typeSig "divMod" $ a --> a --> tuple [a, a]+-- >      , typeSig "div" $ a --> a --> a+-- >      , funBind "div"+-- >          $ matchRhs [var "x", var "y"]+-- >             $ var "fst" @@ (var "divMod" @@ var "x" @@ var "y")+-- >      ]+class'+    :: [HsType'] -- ^ Context+    -> RdrNameStr -- ^ Class name+    -> [RdrNameStr] -- ^ Type parameters+    -> [ClassDecl] -- ^ Class declarations+    -> HsDecl'+class' context name vars decls+    = noExt TyClD $ ClassDecl+            { tcdCtxt = builtLoc $ map builtLoc context+#if MIN_VERSION_ghc(8,6,0)+            , tcdCExt = NoExt+#else+            , tcdFVs = PlaceHolder+#endif+            , tcdLName = typeRdrName name+            , tcdTyVars = mkQTyVars vars+            , tcdFixity = Prefix+            , tcdFDs = [ builtLoc (map typeRdrName xs, map typeRdrName ys)+                       | ClassFunDep xs ys <- decls+                       ]+            , tcdSigs = [builtLoc sig | ClassSig sig <- decls]+            , tcdMeths =+                listToBag [builtLoc bind | ClassDefaultMethod bind <- decls]+            , tcdATs = []  -- Associated types+            , tcdATDefs = []  -- Associated type defaults+            , tcdDocs = []  -- Haddocks+            }++-- | A definition that can appear in the body of an @instance@ declaration.+data RawInstDecl+    = InstSig Sig'+    | InstBind HsBind'++instance HasValBind RawInstDecl where+    sigB = InstSig+    bindB = InstBind++-- | An instance declaration.+--+-- > instance Show Bool where+-- >   show :: Bool -> String -- Requires the InstanceSigs extension+-- >   show True = "True"+-- >   show False = "False"+-- > =====+-- > instance' (var "Show" @@ var "Bool")+-- >   [ typeSig "show" $ var "Bool" --> var "String"+-- >   , funBinds "show"+-- >       [ matchRhs [var "True"] $ string "True"+-- >       , matchRhs [var "False"] $ string "False"+-- >       ]+-- >   ]+instance' :: HsType' -> [RawInstDecl] -> HsDecl'+instance' ty decls = noExt InstD  $ noExt ClsInstD $ ClsInstDecl+    { cid_poly_ty = sigType ty+#if MIN_VERSION_ghc(8,6,0)+    , cid_ext = NoExt+#endif+    , cid_binds = listToBag [builtLoc b | InstBind b <- decls]+    , cid_sigs = [builtLoc sig | InstSig sig <- decls]+    , cid_tyfam_insts = []+    , cid_datafam_insts = []+    , cid_overlap_mode = Nothing+    }++-- | Declares a type synonym.+--+-- > type A a b = B b a+-- > =====+-- > type' "A" ["a", "b"] $ var "B" @@ var "b" @@ var "a"+type' :: RdrNameStr -> [RdrNameStr] -> HsType' -> HsDecl'+type' name vars t =+    noExt TyClD $ withPlaceHolder $ noExt SynDecl (typeRdrName name)+        (mkQTyVars vars)+        Prefix+        (builtLoc t)++newOrDataType ::+    NewOrData -> RdrNameStr -> [RdrNameStr] -> [ConDecl'] -> HsDecl'+newOrDataType newOrData name vars conDecls+    = noExt TyClD $ withPlaceHolder $ withPlaceHolder $+        noExt DataDecl (typeRdrName name)+            (mkQTyVars vars)+            Prefix+            $ noExt HsDataDefn newOrData+                (builtLoc []) Nothing+                Nothing+                (map builtLoc conDecls)+                (builtLoc [])++-- | A newtype declaration.+--+-- > newtype Const a b = Const a+-- > =====+-- > newtype' "Const" ["a", "b"] $ conDecl "Const" [var "a"]+newtype' :: RdrNameStr -> [RdrNameStr] -> ConDecl' -> HsDecl'+newtype' name vars conD = newOrDataType NewType name vars [conD]++-- | A data declaration.+--+-- > data Either a b = Left a | Right b+-- > =====+-- > data' "Either" ["a", "b"]+-- >   [ conDecl "Left" [var "a"]+-- >   , conDecl "Right" [var "b"]+-- >   ]+data' :: RdrNameStr -> [RdrNameStr] -> [ConDecl'] -> HsDecl'+data' = newOrDataType DataType++-- | Declares a Haskell-98-style prefix constructor for a data or type+-- declaration.+--+-- > Foo a Int+-- > =====+-- > conDecl "Foo" [field (var "a"), field (var "Int")]+prefixCon :: RdrNameStr -> [Field] -> ConDecl'+prefixCon name fields = renderCon98Decl name+    $ PrefixCon $ map renderField fields++-- | Declares a Haskell-98-style infix constructor for a data or type+-- declaration.+--+-- > A b :+: C d+-- > =====+-- > infixCon (field (var "A" @@ var "b")) ":+:" (field (Var "C" @@ var "d"))+infixCon :: Field -> RdrNameStr -> Field -> ConDecl'+infixCon f name f' = renderCon98Decl name+    $ InfixCon (renderField f) (renderField f')++-- | Declares Haskell-98-style record constructor for a data or type+-- declaration.+--+-- > A { x :: B, y :: C }+-- > =====+-- > recordCon "A" [("x", var "B"), ("y", var "C")]+recordCon :: RdrNameStr -> [(RdrNameStr, Field)] -> ConDecl'+recordCon name fields = renderCon98Decl name+    $ RecCon $ builtLoc $ map mkLConDeclField fields+  where+    mkLConDeclField (n, f) =+        builtLoc $ noExt ConDeclField+                        [builtLoc $ withPlaceHolder $ noExt FieldOcc $ valueRdrName n]+                        (renderField f)+                        Nothing++-- | An individual argument of a data constructor.  Contains a type for the field,+-- and whether the field is strict or lazy.+data Field = Field+    { fieldType :: HsType'+    , strictness :: SrcStrictness+    }++-- | A field with no explicit strictness annotations.+--+-- > A b+-- > =====+-- > field $ var "A" @@ var "b"+field :: HsType' -> Field+field t = Field t NoSrcStrict++-- | Give a field an explicit strictness annotation.  Overrides any such previous+-- annotations (for example, from 'lazy').+--+-- > !(A b)+-- > =====+-- > strict $ field $ var "A" @@ var "b"+strict :: Field -> Field+strict f = f { strictness = SrcStrict }++-- | Give a field an explicit laziness annotation.  This feature is useful in combination+-- with the @StrictData@ extension.  Overrides any such previous+-- annotations (for example, from 'strict').+--+-- > !(A b)+-- > =====+-- > strict $ field $ var "A" @@ var "b"+lazy :: Field -> Field+lazy f = f { strictness = SrcLazy }++renderField :: Field -> Located HsType'+-- TODO: parenthesizeTypeForApp is an overestimate in the case of+-- rendering an infix or record type.+renderField f = wrap $ parenthesizeTypeForApp $ builtLoc $ fieldType f+  where+    wrap = case strictness f of+        NoSrcStrict -> id+        s -> builtLoc . (noExt HsBangTy $ noSourceText HsSrcBang NoSrcUnpack s)++renderCon98Decl :: RdrNameStr -> HsConDeclDetails' -> ConDecl'+renderCon98Decl name details = noExt ConDeclH98 (typeRdrName name)+#if MIN_VERSION_ghc(8,6,0)+    (builtLoc False)+    []+#else+    Nothing+#endif+    Nothing+    details+    Nothing++-- | A pattern binding.+--+-- > x = y+-- > =====+-- > patBind (var "x") $ rhs $ var "y"+--+-- > (x, y) = e+-- > =====+-- > patBind (tuple [var "x", var "y"]) $ rhs e+--+-- > (x, y)+-- >   | test = (1, 2)+-- >   | otherwise = (2, 3)+-- > =====+-- > patBind (tuple [var "x", var "y"])+-- >   $ guardedRhs+-- >       [ var "test" `guard` tuple [int 1, int 2]+-- >       , var "otherwise" `guard` [int 2, int 3]+-- >       ]+patBind :: Pat' -> RawGRHSs -> HsDecl'+patBind p g =+    bindB+        $ withPlaceHolder+            (withPlaceHolder+                (noExt PatBind (builtPat p) (mkGRHSs g)))+        $ ([],[])
+ src/GHC/SourceGen/Expr.hs view
@@ -0,0 +1,104 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++{-# LANGUAGE CPP #-}+-- | This module provides combinators for constructing Haskell expressions.+module GHC.SourceGen.Expr where++import HsExpr+import Data.String (fromString)+import SrcLoc (unLoc)++import GHC.SourceGen.Binds.Internal+import GHC.SourceGen.Binds+import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal+import GHC.SourceGen.Type.Internal+    ( parenthesizeTypeForApp+    , sigWcType+    , wcType+    )++-- | An overloaded label, as used with the @OverloadedLabels@ extension.+--+-- > #foo+-- > =====+-- > overLabel "foo"+overLabel :: String -> HsExpr'+overLabel = noExt HsOverLabel Nothing . fromString++let' :: [RawValBind] -> HsExpr' -> HsExpr'+let' binds e = noExt HsLet (builtLoc $ valBinds binds) $ builtLoc e++case' :: HsExpr' -> [RawMatch] -> HsExpr'+case' e matches = noExt HsCase (builtLoc e)+                    $ matchGroup CaseAlt matches++lambda :: [Pat'] -> HsExpr' -> HsExpr'+lambda ps e = noExt HsLam $ matchGroup LambdaExpr [matchRhs ps e]++lambdaCase :: [RawMatch] -> HsExpr'+lambdaCase = noExt HsLamCase . matchGroup CaseAlt++if' :: HsExpr' -> HsExpr' -> HsExpr' -> HsExpr'+if' x y z = noExt HsIf Nothing (builtLoc x) (builtLoc y) (builtLoc z)++-- | A MultiWayIf expression.+--+-- > if | f x = "f"+-- >    | g x = "g"+-- >    | otherwise = "h"+-- > =====+-- > multiIf+-- >     [ guardedStmt (var "f" @@ var "x") $ rhs (string "f")+-- >     , guardedStmt (var "g" @@ var "x") $ rhs (string "g")+-- >     , guardedStmt (var "otherwise") $ rhs (string "h")+-- >     ]+multiIf :: [GuardedExpr] -> HsExpr'+multiIf = noExtOrPlaceHolder HsMultiIf . map builtLoc++-- | A do-expression.+--+-- Individual statements may be constructed with '<--' and/or 'stmt'.+--+-- > do+-- >   x <- act+-- >   return x+-- > =====+-- > do' [var "x" <-- var "act", stmt $ var "return" @@ var "x"]+do' :: [Stmt'] -> HsExpr'+do' = withPlaceHolder . noExt HsDo DoExpr . builtLoc . map builtLoc++-- | A type constraint on an expression.+--+-- > e :: t+-- > =====+-- > var "e" @::@ var "t"+(@::@) :: HsExpr' -> HsType' -> HsExpr'+#if MIN_VERSION_ghc(8,8,0)+e @::@ t = noExt ExprWithTySig (builtLoc e) (sigWcType t)+#elif MIN_VERSION_ghc(8,6,0)+e @::@ t = ExprWithTySig (sigWcType t) (builtLoc e)+#else+e @::@ t = ExprWithTySig (builtLoc e) (sigWcType t)+#endif+-- TODO: The Outputable instance prepends extra spaces; I'm not sure why.++-- | Explicit type application.+--+-- > f @ Int+-- > =====+-- > var "f" @@ var "Int"+tyApp :: HsExpr' -> HsType' -> HsExpr'+#if MIN_VERSION_ghc(8,8,0)+tyApp e t = noExt HsAppType (builtLoc e) t'+#elif MIN_VERSION_ghc(8,6,0)+tyApp e t = HsAppType t' (builtLoc e)+#else+tyApp e t = HsAppType (builtLoc e) t'+#endif+  where+    t' = wcType $ unLoc $ parenthesizeTypeForApp $ builtLoc t
+ src/GHC/SourceGen/Expr/Internal.hs view
@@ -0,0 +1,75 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++{-# LANGUAGE CPP #-}+module GHC.SourceGen.Expr.Internal where++#if MIN_VERSION_ghc(8,4,0)+import BasicTypes (IntegralLit(..))+#endif+import HsExpr+import HsLit+import SrcLoc (Located, unLoc)++import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal++parenthesizeExprForApp, parenthesizeExprForOp+    :: Located HsExpr' -> Located HsExpr'+parenthesizeExprForApp e +    | needsExprForApp (unLoc e) = parExpr e+    | otherwise = e+parenthesizeExprForOp e+    | needsExprForOp (unLoc e) = parExpr e+    | otherwise = e++parExpr :: Located HsExpr' -> Located HsExpr'+parExpr = builtLoc . noExt HsPar++#if MIN_VERSION_ghc(8,6,0)+#define WILD_EXT _+#else+#define WILD_EXT+#endif++needsExprForApp, needsExprForOp :: HsExpr' -> Bool+needsExprForOp e = case e of+    -- TODO: more care for literals; only needed for negative numbers?+    HsLit WILD_EXT l -> litNeedsParen l+    HsOverLit WILD_EXT l -> overLitNeedsParen l+    HsLam{} -> True+    HsLamCase{} -> True+    OpApp{} -> True+    NegApp{} -> True+    HsCase{} -> True+    HsIf{} -> True+    HsMultiIf{} -> True+    HsLet{} -> True+    HsDo{} -> True+    ExprWithTySig{} -> True+    _ -> False+needsExprForApp e = case e of+    HsApp{} -> True+    HsStatic{} -> True+    _ -> needsExprForOp e++litNeedsParen :: HsLit' -> Bool+-- For now, ignoring cases that only arrive from internal compiler passes.+-- Furthermore, GHC parses primitive numbers like -3.0# without needing parentheses.+-- So we can uniformly ignore this step.+litNeedsParen _ = False++overLitNeedsParen :: HsOverLit' -> Bool+overLitNeedsParen = needs . ol_val+  where+#if MIN_VERSION_ghc(8,4,0)+    needs (HsIntegral x) = il_neg x+#else+    needs (HsIntegral _ x) = x < 0+#endif+    -- GHC shows fractional values with "%", so wrap them unconditionally.+    needs HsFractional{} = True+    needs _ = False
+ src/GHC/SourceGen/Lit.hs view
@@ -0,0 +1,62 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++{-# LANGUAGE CPP #-}+-- | This module provides combinators for constructing Haskell literals,+-- which may be used in either patterns or expressions.+module GHC.SourceGen.Lit where++import BasicTypes (FractionalLit(..))+#if MIN_VERSION_ghc(8,4,0)+import BasicTypes(IntegralLit(..))+#endif+import HsLit+import HsExpr (noExpr, noSyntaxExpr, HsExpr(..))+import HsPat (Pat(..))+import FastString (fsLit)++import GHC.SourceGen.Lit.Internal+import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal++class HasLit e where+    lit :: HsLit' -> e+    overLit :: HsOverLit' -> e++instance HasLit HsExpr' where+    lit = noExt HsLit+    overLit = noExt HsOverLit++instance HasLit Pat' where+    lit = noExt LitPat+    overLit l = withPlaceHolder+                    $ noExt NPat (builtLoc l) Nothing noSyntaxExpr++char :: HasLit e => Char -> e+char = lit . noSourceText HsChar++string :: HasLit e => String -> e+string = lit . noSourceText HsString . fsLit++-- | Note: this is an *overloaded* integer.+int :: HasLit e => Integer -> e+int n = overLit $ withPlaceHolder $ withPlaceHolder (noExt OverLit il) noExpr+  where+#if MIN_VERSION_ghc(8,4,0)+    il = HsIntegral $ noSourceText IL (n < 0) n+#else+    il = noSourceText HsIntegral n+#endif++-- | Note: this is an *overloaded* rational, e.g., a decimal number.+frac :: HasLit e => Rational -> e+frac x = overLit $ withPlaceHolder $ withPlaceHolder (noExt OverLit $ HsFractional il) noExpr+  where+#if MIN_VERSION_ghc(8,4,0)+    il = noSourceText FL (x < 0) x+#else+    il = FL (show x) x+#endif
+ src/GHC/SourceGen/Lit/Internal.hs view
@@ -0,0 +1,12 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++module GHC.SourceGen.Lit.Internal where++import BasicTypes (SourceText(NoSourceText))++noSourceText :: (SourceText -> a) -> a+noSourceText = ($ NoSourceText)
+ src/GHC/SourceGen/Module.hs view
@@ -0,0 +1,53 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++-- | This module provides combinators for constructing Haskell modules,+-- including import and export statements.+module GHC.SourceGen.Module where++import HsSyn+    ( HsModule(..)+    , ImportDecl(..)+    )++import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal+import GHC.SourceGen.Name.Internal+import GHC.SourceGen.Lit.Internal (noSourceText)++module'+    :: Maybe ModuleNameStr+    -> Maybe [IE'] -- ^ Exports+    -> [ImportDecl']+    -> [HsDecl']+    -> HsModule'+module' name exports imports decls = HsModule+    { hsmodName = fmap (builtLoc . unModuleNameStr) name+    , hsmodExports = fmap (builtLoc . map builtLoc) exports+    , hsmodImports = map builtLoc imports+    , hsmodDecls = fmap builtLoc decls+    , hsmodDeprecMessage = Nothing+    , hsmodHaddockModHeader = Nothing+    }++qualified' :: ImportDecl' -> ImportDecl'+qualified' d = d { ideclQualified = True }++as' :: ImportDecl' -> ModuleNameStr -> ImportDecl'+as' d m = d { ideclAs = Just (builtLoc $ unModuleNameStr m) }++import' :: ModuleNameStr -> ImportDecl'+import' m = noSourceText (noExt ImportDecl)+            (builtLoc $ unModuleNameStr m)+            Nothing False False False False Nothing Nothing++exposing :: ImportDecl' -> [IE'] -> ImportDecl'+exposing d ies = d+    { ideclHiding = Just (False, builtLoc $ map builtLoc ies) }++hiding :: ImportDecl' -> [IE'] -> ImportDecl'+hiding d ies = d+    { ideclHiding = Just (True, builtLoc $ map builtLoc ies) }
+ src/GHC/SourceGen/Name.hs view
@@ -0,0 +1,26 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++-- | This module defines custom types for defining names of various+-- syntax terms.+--+-- These types are all instances of 'Data.String.IsString'.  For easier use,+-- we recommend enabling the @OverloadedStrings@ extension.+module GHC.SourceGen.Name+    ( RdrNameStr(..)+    , OccNameStr+    , ModuleNameStr(..)+    , qual+    , unqual+    ) where++import GHC.SourceGen.Name.Internal++unqual :: OccNameStr -> RdrNameStr+unqual = RawUnqual++qual :: ModuleNameStr -> OccNameStr -> RdrNameStr+qual = RawQual
+ src/GHC/SourceGen/Name/Internal.hs view
@@ -0,0 +1,72 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++module GHC.SourceGen.Name.Internal where++import Data.Char (isUpper)+import Data.String (IsString(..))+import FastString (FastString, fsLit)+import Module (mkModuleNameFS, ModuleName)+import RdrName+import OccName+import SrcLoc (Located)++import GHC.SourceGen.Syntax.Internal (builtLoc)++-- | A string identifier.  This definition is simililar to 'RdrName', but+-- independent of whether it's in the type or value namespace.+data OccNameStr = OccNameStr !RawNameSpace !FastString++data RawNameSpace = Constructor | Value+-- TODO: symbols++rawNameSpace :: String -> RawNameSpace+rawNameSpace (c:_)+    | isUpper c = Constructor+rawNameSpace _ = Value++instance IsString OccNameStr where+    fromString s = OccNameStr (rawNameSpace s) (fsLit s)++valueOccName, typeOccName :: OccNameStr -> OccName+valueOccName (OccNameStr Constructor s) = mkDataOccFS s+valueOccName (OccNameStr Value s) = mkVarOccFS s+typeOccName (OccNameStr Constructor s) = mkTcOccFS s+typeOccName (OccNameStr Value s) = mkTyVarOccFS s++-- | A newtype wrapper around 'ModuleName' which is an instance of 'IsString'.+newtype ModuleNameStr = ModuleNameStr { unModuleNameStr :: ModuleName }++instance IsString ModuleNameStr where+    fromString = ModuleNameStr . mkModuleNameFS . fsLit++-- | A string identifier which may be qualified to a particular module.+--+-- For example:+--+-- > fromString "A.b.c" == RawQual (fromString "A.b") (fromString "c")+-- > fromString "c" == RawUnqual (fromString "c")+--+-- This definition is simililar to 'RdrName', but independent of whether it's+-- in the type or value namespace.  Functions in this package that take+-- a 'RdrName' as input will internally convert it to the proper namespace.+data RdrNameStr = RawUnqual OccNameStr | RawQual ModuleNameStr OccNameStr++-- GHC always wraps RdrName in a Located.  (Usually: 'Located (IdP pass)')+-- So for convenience, these functions return a Located-wrapped value.+valueRdrName, typeRdrName :: RdrNameStr -> Located RdrName+valueRdrName (RawUnqual r) = builtLoc $ Unqual $ valueOccName r+valueRdrName (RawQual (ModuleNameStr m) r) = builtLoc $ Qual m $ valueOccName r+typeRdrName (RawUnqual r) = builtLoc $ Unqual $ typeOccName r+typeRdrName (RawQual (ModuleNameStr m) r) = builtLoc $ Qual m $ typeOccName r++-- TODO: operators+instance IsString RdrNameStr where+    -- Split "Foo.Bar.baz" into ("Foo.Bar", "baz")+    fromString f = case span (/= '.') (reverse f) of+        (f', '.':f'') ->+            RawQual (fromString $ reverse f'') (fromString $ reverse f')+        _ -> RawUnqual (fromString f)
+ src/GHC/SourceGen/Overloaded.hs view
@@ -0,0 +1,235 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++{-# LANGUAGE CPP #-}+-- | This module overloads some combinators so they can be used in+-- different contexts: for expressions, types and/or patterns.+module GHC.SourceGen.Overloaded+    ( Par(..)+    , App(..)+    , HasTuple(..)+    , tuple+    , unboxedTuple+    , HasList(..)+    , Var(..)+    ) where++import BasicTypes (Boxity(..))+import HsTypes+    ( HsType(..)+    , HsTyVarBndr(..)+    )+import HsSyn (IE(..), IEWrappedName(..))+#if !MIN_VERSION_ghc(8,6,0)+import PlaceHolder(PlaceHolder(..))+#endif++import HsSyn+    ( HsExpr(..)+    , Pat(..)+    , HsTupArg(..)+    , HsTupleSort(..)+    )+import DataCon (dataConName)+import RdrName (RdrName, nameRdrName)+import SrcLoc (Located)+import TysWiredIn (consDataCon_RDR, nilDataCon, unitDataCon)++import GHC.SourceGen.Expr.Internal+import GHC.SourceGen.Name.Internal+import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal+import GHC.SourceGen.Type.Internal++-- | A class for wrapping terms in parentheses.+class Par e where+    par :: e -> e++instance Par HsExpr' where+    par = noExt HsPar . builtLoc++instance Par Pat' where+    par = noExt ParPat . builtPat++instance Par HsType' where+    par = noExt HsParTy . builtLoc++-- | A class for term application.+--+-- These functions may add additional parentheses to the AST.+-- GHC's pretty-printing functions expect those parentheses+-- to already be present, because GHC preserves parentheses when it+-- parses the AST from a source file.+class App e where+    -- | Prefix-apply a term:+    --+    -- > f x+    -- > =====+    -- > var "f" @@ var "x"+    --+    -- > (+) x+    -- > =====+    -- > var "+" @@ var "x"+    --+    -- Also parenthesizes the right-hand side in order to preserve its+    -- semantics when pretty-printed, but tries to do so only when+    -- necessary:+    --+    -- > f x y+    -- > =====+    -- > var "f" @@ var "x" @@ var "y"+    -- > -- equivalently:+    -- > (var "f" @@ var "x") @@ var "y"+    --+    -- > f (g x)+    -- > =====+    -- > var "f" @@ (var "g" @@ var "x")+    --+    -- > f (g x)+    -- > =====+    -- > var "f" @@ par (var "g" @@ par (var "x"))+    (@@) :: e -> e -> e++    -- | Infix-apply an operator or function.+    --+    -- For example:+    --+    -- > x + y+    -- > =====+    -- > op (var "x") "+" (var "y")+    --+    -- Also parenthesizes the right-hand side in order to preserve its+    -- semantics when pretty-printed, but tries to do so only when necessary:+    --+    -- > f x + g y+    -- > =====+    -- > op (var "f" @@ var "x") "+" (var "g" @@ var "y")+    --+    -- > x + (y + z)+    -- > =====+    -- > op (var "x") "+" (op (var "y") "+" (var "z"))+    --+    -- > f x `plus` g y+    -- > =====+    -- > op (var "f" @@ var "x") "plus" (var "g" @@ var "y")+    op :: e -> RdrNameStr -> e -> e+infixl 2 @@++instance App HsExpr' where+    op x o y+        = noExt OpApp+            (parenthesizeExprForOp $ builtLoc x)+            (builtLoc $ var o)+#if !MIN_VERSION_ghc(8,6,0)+            PlaceHolder+#endif+            (parenthesizeExprForOp $ builtLoc y)+    x @@ y = noExt HsApp (builtLoc x)+                (parenthesizeExprForApp $ builtLoc y)++instance App HsType' where+    op x o y+        = noExt HsOpTy (parenthesizeTypeForOp $ builtLoc x)+                (typeRdrName o)+                (parenthesizeTypeForOp $ builtLoc y)+    x @@ y = noExt HsAppTy+                (builtLoc x)+                (parenthesizeTypeForApp $ builtLoc y)++class HasTuple e where+    unit :: e+    tupleOf :: Boxity -> [e] -> e++tuple, unboxedTuple :: HasTuple e => [e] -> e+tuple = tupleOf Boxed+unboxedTuple = tupleOf Unboxed++instance HasTuple HsExpr' where+    tupleOf b ts =+        noExt ExplicitTuple+            (map (builtLoc . noExt Present . builtLoc) ts)+            b+    unit = noExt HsVar unitDataConName++unitDataConName :: Located RdrName+unitDataConName = builtLoc $ nameRdrName $ dataConName $ unitDataCon++instance HasTuple HsType' where+    tupleOf b = noExt HsTupleTy b' . map builtLoc+        where+            b' = case b of+                    Unboxed -> HsUnboxedTuple+                    -- See the note [Unit tuples] in HsType.hs for why+                    -- this isn't just HsBoxed.+                    Boxed -> HsBoxedOrConstraintTuple+    unit = tupleOf Boxed []++instance HasTuple Pat' where+    tupleOf b ps =+        noExt TuplePat (map builtPat ps) b+#if !MIN_VERSION_ghc(8,6,0)+        []+#endif+    unit = noExt VarPat unitDataConName++-- | An explicit list of terms.+--+-- > [x, y]+-- > =====+-- > list [var "x", var "y"]+--+-- NOTE: for types, use either @listTy@ or @promotedListTy@.+class HasList e where+    list :: [e] -> e+    -- | The empty list @[]@.+    nil :: e++    -- | The list cons constructor @(:)@.+    cons :: e++-- TODO: allow something like "consOp" which applies (:) as an operator, but using+-- the built-in RdrName.++nilDataConName :: Located RdrName+nilDataConName = builtLoc $ nameRdrName $ dataConName $ nilDataCon++instance HasList HsExpr' where+    list = withPlaceHolder (noExt ExplicitList) Nothing . map builtLoc+    nil = noExt HsVar nilDataConName+    cons = noExt HsVar $ builtLoc consDataCon_RDR++instance HasList Pat' where+#if MIN_VERSION_ghc(8,6,0)+    list = noExt ListPat . map builtPat+#else+    list ps = ListPat (map builtPat ps) PlaceHolder Nothing+#endif+    nil = noExt VarPat nilDataConName+    cons = noExt VarPat $ builtLoc $ consDataCon_RDR++-- | Terms that can contain references to named things.  They may be actual variables,+-- functions, or constructors.  For example, @'var' \"a\"@ and @'var' \"A\"@+-- are equally valid.+-- Depending on the context, the former could refer to either a function,+-- value, type variable, or pattern; and the latter could refer to either a type+-- constructor or a  data constructor,+class Var a where+    var :: RdrNameStr -> a++instance Var Pat' where+    var = noExt VarPat . valueRdrName++instance Var HsExpr' where+    var = noExt HsVar . valueRdrName++instance Var HsType' where+    var = noExt HsTyVar notPromoted . typeRdrName++instance Var HsTyVarBndr' where+    var = noExt UserTyVar . typeRdrName++instance Var IE' where+    var = noExt IEVar . builtLoc . IEName . valueRdrName
+ src/GHC/SourceGen/Pat.hs view
@@ -0,0 +1,51 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++-- | This module provides combinators for constructing Haskell patterns.+module GHC.SourceGen.Pat where++import HsTypes+import HsPat++import GHC.SourceGen.Name.Internal+import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal++-- | A wild pattern (@_@).+wildP :: Pat'+wildP = noExtOrPlaceHolder WildPat++-- | An as-pattern.+--+-- > a@B+-- > =====+-- > asP "a" (var "B")+asP :: RdrNameStr -> Pat' -> Pat'+v `asP` p = noExt AsPat (valueRdrName v) $ builtPat p++-- | A pattern constructor.+--+-- > A b c+-- > =====+-- > conP "A" [var "b", var "c"]+conP :: RdrNameStr -> [Pat'] -> Pat'+conP c xs = ConPatIn (valueRdrName c) $ PrefixCon $ map builtPat xs++-- | A bang-pattern.+--+-- > !x+-- > =====+-- > strictP (var x)+strictP :: Pat' -> Pat'+strictP = noExt BangPat . builtPat++-- | A lazy pattern match.+--+-- > ~(A x)+-- > =====+-- > lazyP (conP "A" [var x])+lazyP :: Pat' -> Pat'+lazyP = noExt LazyPat . builtPat
+ src/GHC/SourceGen/Pretty.hs view
@@ -0,0 +1,25 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++-- | This module provides utilities for rendering GHC syntax as strings.+module GHC.SourceGen.Pretty+    ( showPpr+    , putPpr+    , hPutPpr+    ) where++import DynFlags+import GhcMonad+import Outputable+import System.IO++hPutPpr :: Outputable a => Handle -> a -> Ghc ()+hPutPpr h x = do+    dflags <- getDynFlags+    liftIO $ printForUser dflags h neverQualify $ ppr x++putPpr :: Outputable a => a -> Ghc ()+putPpr = hPutPpr stdout
+ src/GHC/SourceGen/Syntax.hs view
@@ -0,0 +1,109 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{- | This module defines type synonyms for the different parts of GHC's syntax+tree.++GHC uses the same types at different stages of the compilation, distinguishing+them using a type parameter.+The functions in @ghc-source-gen@ construct values as they would appear after the+parsing step.+-}+module GHC.SourceGen.Syntax where++import HsSyn+    ( HsDecl+    , HsExpr(..)+    , HsLit+    , HsModule+    , HsType(..)+    , HsBind+    , HsTyVarBndr+    , HsOverLit+    , HsValBinds+    , HsMatchContext+    , IE+    , LHsQTyVars+    , Match+    , MatchGroup+    , GRHS+    , GRHSs+    , Stmt+    , ConDecl+    , HsConDeclDetails+    , LHsSigType+    , ImportDecl+    , LHsSigWcType+    , LHsWcType+    )+import HsBinds (Sig, HsLocalBinds)+import HsPat+import RdrName (RdrName)+import SrcLoc (Located)++#if MIN_VERSION_ghc(8,4,0)+import HsExtension (GhcPs)+#endif++#if MIN_VERSION_ghc(8,4,0)+type HsExpr' = HsExpr GhcPs+type HsLit' = HsLit GhcPs+type HsType' = HsType GhcPs+type HsDecl' = HsDecl GhcPs+type HsModule' = HsModule GhcPs+type HsBind' = HsBind GhcPs+type HsLocalBinds' = HsLocalBinds GhcPs+type HsValBinds' = HsValBinds GhcPs+type Sig' = Sig GhcPs+type Pat' = Pat GhcPs+type HsMatchContext' = HsMatchContext RdrName+type Match' = Match GhcPs+type MatchGroup' = MatchGroup GhcPs+type GRHS' = GRHS GhcPs+type GRHSs' = GRHSs GhcPs+type Stmt' = Stmt GhcPs (Located HsExpr')+type HsTyVarBndr' = HsTyVarBndr GhcPs+type HsOverLit' = HsOverLit GhcPs+type LHsQTyVars' = LHsQTyVars GhcPs+type ConDecl' = ConDecl GhcPs+type HsConDeclDetails' = HsConDeclDetails GhcPs+type LHsSigType' = LHsSigType GhcPs+type IE' = IE GhcPs+type ImportDecl' = ImportDecl GhcPs+type LHsSigWcType' = LHsSigWcType GhcPs+type LHsWcType' = LHsWcType GhcPs++#else+type HsExpr' = HsExpr RdrName+type HsLit' = HsLit+type HsType' = HsType RdrName+type HsDecl' = HsDecl RdrName+type HsModule' = HsModule RdrName+type HsBind' = HsBind RdrName+type HsLocalBinds' = HsLocalBinds RdrName+type HsValBinds' = HsValBinds RdrName+type Sig' = Sig RdrName+type Pat' = Pat RdrName+type HsMatchContext' = HsMatchContext RdrName+type Match' = Match RdrName+type MatchGroup' = MatchGroup RdrName+type GRHS' = GRHS RdrName+type GRHSs' = GRHSs RdrName+type Stmt' = Stmt RdrName (Located HsExpr')+type HsTyVarBndr' = HsTyVarBndr RdrName+type HsOverLit' = HsOverLit RdrName+type LHsQTyVars' = LHsQTyVars RdrName+type ConDecl' = ConDecl RdrName+type HsConDeclDetails' = HsConDeclDetails RdrName+type LHsSigType' = LHsSigType RdrName+type IE' = IE RdrName+type ImportDecl' = ImportDecl RdrName+type LHsSigWcType' = LHsSigWcType RdrName+type LHsWcType' = LHsWcType RdrName++#endif
+ src/GHC/SourceGen/Syntax/Internal.hs view
@@ -0,0 +1,73 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+module GHC.SourceGen.Syntax.Internal where++import SrcLoc (SrcSpan, Located, GenLocated(..), mkGeneralSrcSpan)++#if MIN_VERSION_ghc(8,8,0)+import BasicTypes (PromotionFlag(..))+#else+import HsTypes (Promoted(..))+#endif++#if MIN_VERSION_ghc(8,6,0)+import HsExtension (NoExt(NoExt))+#else+import PlaceHolder(PlaceHolder(..))+#endif+import GHC.SourceGen.Syntax++#if MIN_VERSION_ghc(8,6,0)+noExt :: (NoExt -> a) -> a+noExt = ($ NoExt)++noExtOrPlaceHolder :: (NoExt -> a) -> a+noExtOrPlaceHolder = noExt++withPlaceHolder :: a -> a+withPlaceHolder = id++#else++noExt :: a -> a+noExt = id++noExtOrPlaceHolder :: (PlaceHolder -> a) -> a+noExtOrPlaceHolder = withPlaceHolder++withPlaceHolder :: (PlaceHolder -> a) -> a+withPlaceHolder = ($ PlaceHolder)++#endif++builtSpan :: SrcSpan+builtSpan = mkGeneralSrcSpan "<ghc-source-gen>"++builtLoc :: e -> Located e+builtLoc = L builtSpan++-- In GHC-8.8, source locations for Pat aren't stored in each node, and+-- LPat is a synonym for Pat.+#if MIN_VERSION_ghc(8,8,0)+builtPat :: Pat' -> Pat'+builtPat = id+#else+builtPat :: Pat' -> Located Pat'+builtPat = builtLoc+#endif++#if MIN_VERSION_ghc(8,8,0)+promoted, notPromoted :: PromotionFlag+promoted = IsPromoted+notPromoted = NotPromoted+#else+promoted, notPromoted :: Promoted+promoted = Promoted+notPromoted = NotPromoted+#endif
+ src/GHC/SourceGen/Type.hs view
@@ -0,0 +1,54 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++-- | This module provides combinators for constructing Haskell types.+module GHC.SourceGen.Type where++import Data.String (fromString)+import HsTypes++import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal+import GHC.SourceGen.Lit.Internal (noSourceText)+import GHC.SourceGen.Name.Internal+import GHC.SourceGen.Type.Internal++-- | A promoted name, for example from the @DataKinds@ extension.+tyPromotedVar :: RdrNameStr -> HsType'+tyPromotedVar = noExt HsTyVar notPromoted . typeRdrName++stringTy :: String -> HsType'+stringTy = noExt HsTyLit . noSourceText HsStrTy . fromString++numTy :: Integer -> HsType'+numTy = noExt HsTyLit . noSourceText HsNumTy++listTy :: HsType' -> HsType'+listTy = noExt HsListTy . builtLoc++listPromotedTy :: [HsType'] -> HsType'+listPromotedTy = withPlaceHolder (noExt HsExplicitListTy notPromoted) . map builtLoc++-- | A function type.+--+-- > a -> b+-- > =====+-- > var "a" --> var "b"+(-->) :: HsType' -> HsType' -> HsType'+a --> b = noExt HsFunTy (parenthesizeTypeForFun $ builtLoc a) (builtLoc b)++infixr 0 -->++forall' :: [HsTyVarBndr'] -> HsType' -> HsType'+forall' ts = noExt HsForAllTy (map builtLoc ts) . builtLoc++-- | Qualify a type with constraints.+--+-- > (F x, G x) => x+-- > =====+-- > [var "F" @@ var "x", var "G" @@ var "x"] ==> var "x"+(==>) :: [HsType'] -> HsType' -> HsType'+(==>) cs = noExt HsQualTy (builtLoc (map builtLoc cs)) . builtLoc
+ src/GHC/SourceGen/Type/Internal.hs view
@@ -0,0 +1,64 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++{-# LANGUAGE CPP #-}+module GHC.SourceGen.Type.Internal where++import HsTypes+import SrcLoc (Located, unLoc)++import GHC.SourceGen.Syntax+import GHC.SourceGen.Syntax.Internal+import GHC.SourceGen.Name.Internal++mkQTyVars :: [RdrNameStr] -> LHsQTyVars'+mkQTyVars vars =  withPlaceHolder+                $ noExt (withPlaceHolder HsQTvs)+                $ map (builtLoc . noExt UserTyVar . typeRdrName)+                    vars++sigType :: HsType' -> LHsSigType'+sigType t = withPlaceHolder $ noExt (withPlaceHolder HsTypes.HsIB) $ builtLoc t+++-- TODO: GHC >= 8.6 provides parenthesizeHsType.  For consistency with+-- older versions, we're implementing our own parenthesis-wrapping.+-- Once we stop supporting GHC-8.4, we can switch to that implementation.++parenthesizeTypeForApp, parenthesizeTypeForOp, parenthesizeTypeForFun+    :: Located HsType' -> Located HsType'+parenthesizeTypeForApp t+    | needsParenForApp (unLoc t) = parTy t+    | otherwise = t+parenthesizeTypeForOp t+    | needsParenForOp (unLoc t) = parTy t+    | otherwise = t+parenthesizeTypeForFun t+    | needsParenForFun (unLoc t) = parTy t+    | otherwise = t++needsParenForFun, needsParenForOp, needsParenForApp+    :: HsType' -> Bool+needsParenForFun t = case t of+    HsForAllTy{} -> True+    HsQualTy{} -> True+    HsFunTy{} -> True+    _ -> False+needsParenForOp t = case t of+    HsOpTy{} -> True+    _ -> needsParenForFun t+needsParenForApp t = case t of+    HsAppTy {} -> True+    _ -> needsParenForOp t++parTy :: Located HsType' -> Located HsType'+parTy = builtLoc . noExt HsParTy++sigWcType :: HsType' -> LHsSigWcType'+sigWcType = noExt (withPlaceHolder HsTypes.HsWC) . sigType++wcType :: HsType' -> LHsWcType'+wcType = noExt (withPlaceHolder HsTypes.HsWC) . builtLoc
+ tests/pprint_examples.hs view
@@ -0,0 +1,220 @@+-- Copyright 2019 Google LLC+--+-- Use of this source code is governed by a BSD-style+-- license that can be found in the LICENSE file or at+-- https://developers.google.com/open-source/licenses/bsd++-- | Simple example using this package+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+module Main (main) where++import GHC.Paths (libdir)+import GHC (runGhc)+import Outputable (Outputable)++import GHC.SourceGen++main :: IO ()+main = mapM_ run [test1, test2, test3, test4, test5]+  where+    run act = putStrLn "========" >> act++pprint :: Outputable a => a -> IO ()+pprint x = runGhc (Just libdir) $ putPpr x++test1 :: IO ()+test1 = pprint $ tuple+    [ var "Foo.abc"+    , overLabel "def"+    , char 'g'+    , let' [ typeSig "result" $ var "A" @@ var "B"+           , funBind "result"+                $ matchRhs [var "x", wildP]+                    $ var "foo" @@ char 'c'+           ]+        (var "result")+    ]++test2 :: IO ()+test2 = pprint $ module' (Just "Foo") (Just [var "efg"]) []+    [ typeSigs ["efg", "h"] $ tuple [var "A", var "B"]+    , funBind "efg"+        $ match []+        $ rhs (char 'a')+            `where'` [ typeSig "q" $ var "Q"+                     , funBind "q" $ match []+                        $ guardedRhs [var "True" `guard` char 'q']+                     ]+    , funBind "f"+        $ match [var "x", var "y"]+        $ rhs+            (case' (var "y")+                        [matchRhs [wildP] $ var "x"])+            `where'` [funBind "q" $ matchRhs [] $ char 't']+    ]++test3 :: IO ()+test3 = pprint $ module' Nothing Nothing []+    [ funBind "lambdas" $ matchRhs [] $ lambda [var "y"]+                    $ lambdaCase [matchRhs [var "z"] (char 'a')]+    , funBinds "ifs"+        [ matchRhs [var "x"] $ if' (var "b") (var "t") (var "f")+        , matchRhs [var "y"] $ multiIf [guard (var "False") $ char 'f'+                                       , guard (var "True") $ char 't'+                                       ]+        , matchRhs [var "z"] $ multiIf+            [ guard (var "f" @@ var "x") $ string "f"+            , guard (var "g" @@ var "x") $ string "g"+            , guard (var "otherwise") $ string "h"+            ]+        ]+    , funBind "do'"+        $ matchRhs [] (do' [ var "x" <-- var "act"+                        , stmt $ var "return" @@ var "x"+                        ])+    , typeSig "types"+        $ forall' [var "x", var "y"]+        $ [var "Show" @@ var "x"] ==> var "y"+    , typeSig "types'"+        $ [var "Show" @@ var "x"] ==>+            (forall' [var "x", var "y"]+                $ var "y")+    , funBind "swap"+        $ matchRhs [tuple [var "x", var "y"]]+            $ tuple [var "y", var "x"]+    , funBind "char" $ matchRhs [char 'a'] (char 'b')+    , funBind "string" $ matchRhs [string "abc"] (string "def")+    , funBind "as"+        $ matchRhs [asP "x" (tuple [var "y", var "z"])]+            (var "x")+    , funBind "con"+        $ matchRhs [conP "A" [var "b", conP "C" [var "d"]]]+            $ tuple [var "b", var "d"]+    , funBind "ops"+        $ matchRhs [var "x", var "y"]+            $ op (var "x") "+" (var "y")+    , funBinds "ops'"+        [ matchRhs [] (op (int 1) "*"+                        (op (int 2) "+" (int 3)))+        , matchRhs [] (op (var "A" @@ var "x") "*"+                        (op (var "B" @@ var "y") "+"+                                 (var "C" @@ var "z")))+        , matchRhs [] (op (var "A" @@ var "x") "mult"+                        (op (var "B" @@ var "y") "+"+                                 (var "C" @@ var "z")))+        ]+    , funBinds "cons'"+        [ matchRhs [] (var "X" @@ int 1 @@+                        (var "Y" @@ int 2 @@ int 3)+                        @@ var "Z")+        , matchRhs [] (var "f" @@ par (var "g" @@ var "x"))+        ]+    , typeSig "f" $ var "X" @@ var "a" @@+                        (var "Y" @@ var "b" @@ var "c")+                        @@ var "Z"+    , typeSig "g" $ op (var "A" @@ var "x") "*"+                        (op (var "B" @@ var "y") "+"+                                 (var "C" @@ var "z"))+    , class' [var "A" @@ var "a"] "B" ["b", "b'"]+        [ typeSig "f" $ var "b" --> var "b'"+        , funBind "f" $ matchRhs [] $ var "id"+        ]+    , class' [] "F" ["a", "b", "c"]+        [ funDep ["a", "b"] ["c"]+        , funDep ["a"] ["b", "c"]+        ]+    , class' [] "Ident" ["a", "b"]+        [ funDep ["a"] ["b"]+        , funDep ["b"] ["a"]+        , typeSig "ident" $ var "a" --> var "b"+        ]+    , type' "A" ["b", "c"] $ var "D"+    , data' "A" ["b", "c"]+        [ prefixCon "A" [field (var "b"), field (var "c")]+        , prefixCon "D" []+        ]+    , newtype' "A" ["b", "c"] (prefixCon "A" [field (var "b")])+    , instance' (var "A" @@ var "b" @@ var "c")+        [ typeSig "f" $ var "b" --> var "c"+        , funBind "f" $ matchRhs [] $ var "undefined"+        ]+    , let a = var "a"+      in class'+           [var "Real" @@ a, var "Enum" @@ a]+           "Integral"+           ["a"]+           [ typeSig "divMod" $ a --> a --> tuple [a, a]+           , typeSig "div" $ a --> a --> a+           , funBind "div"+               $ matchRhs [var "x", var "y"]+                  $ var "fst" @@ (var "divMod" @@ var "x" @@ var "y")+           ]+    , instance' (var "Show" @@ var "Bool")+        [ typeSig "show" $ var "Bool" --> var "String"+        , funBinds "show"+            [ matchRhs [var "True"] $ string "True"+            , matchRhs [var "False"] $ string "False"+            ]+        ]+    , data' "X" ["b"]+        [ prefixCon "X"+            [ field $ var "A" @@ var "b"+            , strict $ field $ var "A" @@ var "b"+            , lazy $ field $ var "A" @@ var "b"+            ]+        , prefixCon ":+"+            [ field $ var "A" @@ var "b"+            , strict $ field $ var "A" @@ var "b"+            , lazy $ field $ var "A" @@ var "b"+            ]+        , infixCon+            (strict $ field $ var "A" @@ var "b")+            "Y"+            (lazy $ field $ var "A" @@ var "b")+        , infixCon+            (strict $ field $ var "A" @@ var "b")+            ":*"+            (lazy $ field $ var "A" @@ var "b")+        , infixCon+            (field $ var "A" @@ var "b")+            ":*"+            (field $ var "A" @@ var "b")+        , recordCon "Z"+            [ ("x", field $ var "Int")+            , ("y", field $ var "A" @@ var "b")+            , ("y", strict $ field $ var "A" @@ var "b")+            , ("y", lazy $ field $ var "A" @@ var "b")+            ]+        ]+    , funBind "strictness"+        $ matchRhs+            [strictP (conP "A" [var "b"]),+             lazyP (conP "A" [var "b"])+            ] (char 'x')+    , typeSig "unit" $ unit --> unit+    , funBind "unit" $ matchRhs [unit] unit+    ]++test4 :: IO ()+test4 = pprint constModule++test5 :: IO ()+test5 = pprint $ module' (Just "M") (Just exports) imports []+  where+    exports = [var "a", var "b"]+    imports = [ qualified' $ import' "A"+              , import' "B" `as'` "C"+              , import' "D" `exposing` [var "d"]+              , import' "E" `hiding` [var "e"]+              ]++constModule :: HsModule'+constModule = module' (Just "Const") (Just [var "const"]) []+    [ typeSig "const" $ a --> b --> a+    , funBind "const" $ matchRhs [wildP, x] x+    ]+  where+    a = var "a"+    b = var "b"+    x = var "x"
+ tests/pprint_test.hs view
@@ -0,0 +1,176 @@+{-# LANGUAGE OverloadedStrings #-}+module Main(main) where++import DynFlags (getDynFlags)+import GhcMonad (liftIO)+import GHC.Paths (libdir)+import GHC (runGhc, DynFlags)+import Outputable (Outputable)++import Test.Tasty+import Test.Tasty.HUnit++import GHC.SourceGen++data TestCase a = String :~ a++infixr 0 :~++testCases :: Outputable a => DynFlags -> String -> [TestCase a] -> TestTree+testCases dflags name cases = testCase name $ mapM_ run cases+  where+    run (expected :~ x) = expected @=? showPpr dflags x++testTypes :: DynFlags ->  String -> [TestCase HsType'] -> TestTree+testTypes = testCases++testExprs :: DynFlags ->  String -> [TestCase HsExpr'] -> TestTree+testExprs = testCases++testDecls :: DynFlags ->  String -> [TestCase HsDecl'] -> TestTree+testDecls = testCases++main :: IO ()+main = runGhc (Just libdir) $ do+    dflags <- getDynFlags+    liftIO $ defaultMain $ testGroup "Tests"+        [typesTest dflags, exprsTest dflags, declsTest dflags]++typesTest, exprsTest, declsTest :: DynFlags -> TestTree+typesTest dflags = testGroup "Type"+    [ test "var"+        [ "A" :~ var "A"+        , "x" :~ var "x"+        , "A.x" :~ var "A.x"+        , "x" :~ var (unqual "x")+        , "A.x" :~ var (qual "A" "x")+        ]+    , test "app"+        [ "A x" :~ var "A" @@ var "x"+        , "(+) x" :~ var "+" @@ var "x"+        , "A (B x)" :~ var "A" @@ par (var "B" @@ var "x")+        , "A x (B y z)" :~ var "A" @@ var "x" @@ (var "B" @@ var "y" @@ var "z")+        , "A w (B x y) Z"+            :~ var "A" @@ var "w" @@ (var "B" @@ var "x" @@ var "y") @@ var "Z"+        ]+    , test "op"+        [ "x + y" :~ op (var "x") "+" (var "y")+        , "x `add` y" :~ op (var "x") "add" (var "y")+        , "x * (y + z)" :~ op (var "x") "*" (op (var "y") "+" (var "z"))+        , "x `mult` (y `add` z)" :~ op (var "x") "mult" (op (var "y") "add" (var "z"))+        , "A x * (B y + C z)" :~ op (var "A" @@ var "x") "*"+                                    (op (var "B" @@ var "y") "+" (var "C" @@ var "z"))+        ]+    , test "function"+        [ "a -> b" :~ var "a" --> var "b"+        , "a -> b -> c" :~ var "a" --> var "b" --> var "c"+        , "a -> b -> c" :~ var "a" --> (var "b" --> var "c")+        , "(a -> b) -> c" :~ (var "a" --> var "b") --> var "c"+        ]+    , test "literals"+        [ "\"abc\"" :~ stringTy "abc"+        , "123" :~ numTy 123+        ]+    , test "unit"+        [ "()" :~ unit ]+   , test "list"+        [ "[x]" :~ listTy (var "x")+        , "[]" :~ listPromotedTy []+        , "[x]" :~ listPromotedTy [var "x"]+        , "[y, z]" :~ listPromotedTy [var "y", var "z"]+        ]+    ]+  where+    test = testTypes dflags++exprsTest dflags = testGroup "Expr"+    [ test "var"+        [ "A" :~ var "A"+        , "x" :~ var "x"+        , "A.x" :~ var "A.x"+        , "x" :~ var (unqual "x")+        , "A.x" :~ var (qual "A" "x")+        ]+    , test "app"+        [ "A x" :~ var "A" @@ var "x"+        , "(+) x" :~ var "+" @@ var "x"+        , "A (B x)" :~ var "A" @@ par (var "B" @@ var "x")+        , "A x (B y z)" :~ var "A" @@ var "x" @@ (var "B" @@ var "y" @@ var "z")+        , "A w (B x y) Z"+            :~ var "A" @@ var "w" @@ (var "B" @@ var "x" @@ var "y") @@ var "Z"+        , "A 3" :~ var "A" @@ int 3+        , "A (-3)" :~ var "A" @@ int (-3)+        , "A (3 % 1)" :~ var "A" @@ frac 3.0+        , "A ((-3) % 1)" :~ var "A" @@ frac (-3.0)+        , "A 'x'" :~ var "A" @@ char 'x'+        , "A \"xyz\"" :~ var "A" @@ string "xyz"+        ]+    , test "op"+        [ "x + y" :~ op (var "x") "+" (var "y")+        , "x `add` y" :~ op (var "x") "add" (var "y")+        , "x * (y + z)" :~ op (var "x") "*" (op (var "y") "+" (var "z"))+        , "x `mult` (y `add` z)" :~ op (var "x") "mult" (op (var "y") "add" (var "z"))+        , "A x * (B y + C z)" :~ op (var "A" @@ var "x") "*"+                                    (op (var "B" @@ var "y") "+" (var "C" @@ var "z"))+        ]+    , test ":@@:"+        -- TODO: GHC puts extra space here.+        [ "  e :: t" :~ var "e" @::@ var "t" ]+    , test "unit"+        [ "()" :~ unit ]+    , test "list"+        [ "[]" :~ list []+        , "[]" :~ nil+        , "[x]" :~ list [var "x"]+        , "[y, z]" :~ list [var "y", var "z"]+        , "(:)" :~ cons+        , "(:) x y" :~ cons @@ var "x" @@ var "y"+        ]+    , test "tyApp"+        [ "x @a" :~ tyApp (var "x") (var "a")+        , "x @a @b" :~ tyApp (tyApp (var "x") (var "a")) (var "b")+        , "x @a b" :~ tyApp (var "x") (var "a") @@ var "b"+        , "x @(a b)" :~ tyApp (var "x") (var "a" @@ var "b")+        , "x @(a + b)" :~ tyApp (var "x") (op (var "a") "+" (var "b"))+        ]+    ]+  where+    test = testExprs dflags++declsTest dflags = testGroup "Decls"+    [ test "patBind"+        [ "x = x" :~ patBind (var "x") (rhs $ var "x")+        , "(x, y) = (y, x)" :~ patBind (tuple [var "x", var "y"])+                                    (rhs $ tuple [var "y", var "x"])+        , "(x, y)\n  | test = (1, 2)\n  | otherwise = (2, 3)" :~+            patBind (tuple [var "x", var "y"])+                $ guardedRhs+                    [ var "test" `guard` tuple [int 1, int 2]+                        , var "otherwise" `guard` tuple [int 2, int 3]+                    ]+        , "z | Just y <- x, y = ()" :~+            patBind (var "z")+                $ guardedRhs+                    [guards+                        [ conP "Just" [var "y"] <-- var "x"+                        , stmt (var "y")+                        ]+                        unit+                    ]+        ]+    , test "funBind"+        [ "not True = False\nnot False = True" :~+             funBinds "not"+                [ matchRhs [var "True"] (var "False")+                , matchRhs [var "False"] (var "True")+                ]+        , "not x\n  | x = False\n  | otherwise = True" :~+            funBind "not"+                $ match [var "x"] $ guardedRhs+                    [ guard (var "x") (var "False")+                    , guard (var "otherwise") (var "True")+                    ]+        ]+    ]+  where+    test = testDecls dflags