purescript-0.15.15: src/Language/PureScript/Sugar/DoNotation.hs
-- | This module implements the desugaring pass which replaces do-notation statements with
-- appropriate calls to bind.
module Language.PureScript.Sugar.DoNotation (desugarDoModule) where
import Prelude
import Control.Applicative ((<|>))
import Control.Monad.Error.Class (MonadError(..))
import Control.Monad.Supply.Class (MonadSupply)
import Data.Maybe (fromMaybe)
import Data.Monoid (First(..))
import Language.PureScript.AST (Binder(..), CaseAlternative(..), Declaration, DoNotationElement(..), Expr(..), pattern MkUnguarded, Module(..), SourceSpan, pattern ValueDecl, WhereProvenance(..), binderNames, declSourceSpan, everywhereOnValuesM)
import Language.PureScript.Crash (internalError)
import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), errorMessage, errorMessage', parU, rethrowWithPosition)
import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, Qualified(..), byMaybeModuleName, freshIdent')
import Language.PureScript.Constants.Libs qualified as C
-- | Replace all @DoNotationBind@ and @DoNotationValue@ constructors with
-- applications of the bind function in scope, and all @DoNotationLet@
-- constructors with let expressions.
desugarDoModule :: forall m. (MonadSupply m, MonadError MultipleErrors m) => Module -> m Module
desugarDoModule (Module ss coms mn ds exts) = Module ss coms mn <$> parU ds desugarDo <*> pure exts
-- | Desugar a single do statement
desugarDo :: forall m. (MonadSupply m, MonadError MultipleErrors m) => Declaration -> m Declaration
desugarDo d =
let ss = declSourceSpan d
(f, _, _) = everywhereOnValuesM return (replace ss) return
in rethrowWithPosition ss $ f d
where
bind :: SourceSpan -> Maybe ModuleName -> Expr
bind ss m = Var ss (Qualified (byMaybeModuleName m) (Ident C.S_bind))
discard :: SourceSpan -> Maybe ModuleName -> Expr
discard ss m = Var ss (Qualified (byMaybeModuleName m) (Ident C.S_discard))
replace :: SourceSpan -> Expr -> m Expr
replace pos (Do m els) = go pos m els
replace _ (PositionedValue pos com v) = PositionedValue pos com <$> rethrowWithPosition pos (replace pos v)
replace _ other = return other
stripPositionedBinder :: Binder -> (Maybe SourceSpan, Binder)
stripPositionedBinder (PositionedBinder ss _ b) =
let (ss', b') = stripPositionedBinder b
in (ss' <|> Just ss, b')
stripPositionedBinder b =
(Nothing, b)
go :: SourceSpan -> Maybe ModuleName -> [DoNotationElement] -> m Expr
go _ _ [] = internalError "The impossible happened in desugarDo"
go _ _ [DoNotationValue val] = return val
go pos m (DoNotationValue val : rest) = do
rest' <- go pos m rest
return $ App (App (discard pos m) val) (Abs (VarBinder pos UnusedIdent) rest')
go _ _ [DoNotationBind _ _] = throwError . errorMessage $ InvalidDoBind
go _ _ (DoNotationBind b _ : _) | First (Just ident) <- foldMap fromIdent (binderNames b) =
throwError . errorMessage $ CannotUseBindWithDo (Ident ident)
where
fromIdent (Ident i) | i `elem` [ C.S_bind, C.S_discard ] = First (Just i)
fromIdent _ = mempty
go pos m (DoNotationBind binder val : rest) = do
rest' <- go pos m rest
let (mss, binder') = stripPositionedBinder binder
let ss = fromMaybe pos mss
case binder' of
NullBinder ->
return $ App (App (bind pos m) val) (Abs (VarBinder ss UnusedIdent) rest')
VarBinder _ ident ->
return $ App (App (bind pos m) val) (Abs (VarBinder ss ident) rest')
_ -> do
ident <- freshIdent'
return $ App (App (bind pos m) val) (Abs (VarBinder pos ident) (Case [Var pos (Qualified ByNullSourcePos ident)] [CaseAlternative [binder] [MkUnguarded rest']]))
go _ _ [DoNotationLet _] = throwError . errorMessage $ InvalidDoLet
go pos m (DoNotationLet ds : rest) = do
let checkBind :: Declaration -> m ()
checkBind (ValueDecl (ss, _) i@(Ident name) _ _ _)
| name `elem` [ C.S_bind, C.S_discard ] = throwError . errorMessage' ss $ CannotUseBindWithDo i
checkBind _ = pure ()
mapM_ checkBind ds
rest' <- go pos m rest
return $ Let FromLet ds rest'
go _ m (PositionedDoNotationElement pos com el : rest) = rethrowWithPosition pos $ PositionedValue pos com <$> go pos m (el : rest)