ghc-source-gen-0.2: src/GHC/SourceGen/Expr.hs
-- 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
( HsExpr'
, overLabel
, let'
, case'
, lambda
, lambdaCase
, if'
, multiIf
, do'
, Stmt'
, (@::@)
, tyApp
, recordConE
, recordUpd
) where
import HsExpr
import HsPat (HsRecField'(..), HsRecFields(..))
import HsTypes (FieldOcc(..), AmbiguousFieldOcc(..))
import Data.String (fromString)
import SrcLoc (unLoc, GenLocated(..), Located)
import GHC.SourceGen.Binds.Internal
import GHC.SourceGen.Binds
import GHC.SourceGen.Expr.Internal
import GHC.SourceGen.Name.Internal
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 [match 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' [bvar "x" <-- var "act", stmt $ var "return" @@ var "x"]
do' :: [Stmt'] -> HsExpr'
do' = withPlaceHolder . noExt HsDo DoExpr
. builtLoc . map (builtLoc . parenthesizeIfLet)
where
-- Put parentheses around a "let" in a do-binding, to avoid:
-- do let x = ...
-- in x
-- which is not valid Haskell.
#if MIN_VERSION_ghc(8,6,0)
parenthesizeIfLet (BodyStmt ext e@(L _ HsLet{}) x y)
= BodyStmt ext (parExpr e) x y
#else
parenthesizeIfLet (BodyStmt e@(L _ HsLet{}) x y tc)
= BodyStmt (parExpr e) x y tc
#endif
parenthesizeIfLet s = s
-- | 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 e' t'
#elif MIN_VERSION_ghc(8,6,0)
tyApp e t = HsAppType t' e'
#else
tyApp e t = HsAppType e' t'
#endif
where
t' = wcType $ unLoc $ parenthesizeTypeForApp $ builtLoc t
e' = builtLoc e
-- | Constructs a record with explicit field names.
--
-- > A { x = y }
-- > =====
-- > recordConE "A" [("x", var "y")]
recordConE :: RdrNameStr -> [(RdrNameStr, HsExpr')] -> HsExpr'
recordConE c fs = (withPlaceHolder $ noExt RecordCon (valueRdrName c))
#if !MIN_VERSION_ghc(8,6,0)
noPostTcExpr
#endif
$ HsRecFields (map recField fs)
Nothing -- No ".."
where
recField :: (RdrNameStr, HsExpr') -> LHsRecField' (Located HsExpr')
recField (f, e) =
builtLoc HsRecField
{ hsRecFieldLbl =
builtLoc $ withPlaceHolder $ noExt FieldOcc $ valueRdrName f
, hsRecFieldArg = builtLoc e
, hsRecPun = False
}
-- | Updates a record expression with explicit field names.
--
-- > r {a = b, c = d}
-- > =====
-- > recordUpd (var "x") [("a", var "b", ("c", var "d"))]
--
-- > (f x) {a = b}
-- > =====
-- > recordUpd (var "f" @@ var "x") [("a", var "b")]
--
-- > f x {a = b} -- equivalent to f (x {a = b})
-- > =====
-- > var "f" @@ recordUpd (var "x") [("a", var "b")]
recordUpd :: HsExpr' -> [(RdrNameStr, HsExpr')] -> HsExpr'
recordUpd e fs =
withPlaceHolder4
$ noExt RecordUpd (parenthesizeExprForApp $ builtLoc e)
$ map mkField fs
where
mkField :: (RdrNameStr, HsExpr') -> LHsRecUpdField'
mkField (f, e') =
builtLoc HsRecField
{ hsRecFieldLbl =
builtLoc $ withPlaceHolder $ noExt Ambiguous $ valueRdrName f
, hsRecFieldArg = builtLoc e'
, hsRecPun = False
}
withPlaceHolder4 = withPlaceHolder . withPlaceHolder . withPlaceHolder
. withPlaceHolder