{-# LANGUAGE PatternGuards #-}
module Language.PureScript.Sugar.DoNotation (desugarDoModule) where
import Prelude.Compat
import Control.Monad.Error.Class (MonadError(..))
import Control.Monad.Supply.Class
import Data.Monoid (First(..))
import Language.PureScript.AST
import Language.PureScript.Crash
import Language.PureScript.Errors
import Language.PureScript.Names
import qualified Language.PureScript.Constants as C
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
desugarDo :: forall m. (MonadSupply m, MonadError MultipleErrors m) => Declaration -> m Declaration
desugarDo d =
let (f, _, _) = everywhereOnValuesM return replace return
in rethrowWithPosition (declSourceSpan d) $ f d
where
bind :: Expr
bind = Var nullSourceSpan (Qualified Nothing (Ident C.bind))
discard :: Expr
discard = Var nullSourceSpan (Qualified Nothing (Ident C.discard))
replace :: Expr -> m Expr
replace (Do els) = go els
replace (PositionedValue pos com v) = PositionedValue pos com <$> rethrowWithPosition pos (replace v)
replace other = return other
go :: [DoNotationElement] -> m Expr
go [] = internalError "The impossible happened in desugarDo"
go [DoNotationValue val] = return val
go (DoNotationValue val : rest) = do
rest' <- go rest
return $ App (App discard val) (Abs (VarBinder nullSourceSpan 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.bind, C.discard ] = First (Just i)
fromIdent _ = mempty
go (DoNotationBind (VarBinder ss ident) val : rest) = do
rest' <- go rest
return $ App (App bind val) (Abs (VarBinder ss ident) rest')
go (DoNotationBind binder val : rest) = do
rest' <- go rest
ident <- freshIdent'
return $ App (App bind val) (Abs (VarBinder nullSourceSpan ident) (Case [Var nullSourceSpan (Qualified Nothing ident)] [CaseAlternative [binder] [MkUnguarded rest']]))
go [DoNotationLet _] = throwError . errorMessage $ InvalidDoLet
go (DoNotationLet ds : rest) = do
let checkBind :: Declaration -> m ()
checkBind (ValueDecl (ss, _) i@(Ident name) _ _ _)
| name `elem` [ C.bind, C.discard ] = throwError . errorMessage' ss $ CannotUseBindWithDo i
checkBind _ = pure ()
mapM_ checkBind ds
rest' <- go rest
return $ Let FromLet ds rest'
go (PositionedDoNotationElement pos com el : rest) = rethrowWithPosition pos $ PositionedValue pos com <$> go (el : rest)