packages feed

retrie-1.0.0.0: Retrie/Rewrites/Patterns.hs

-- Copyright (c) Facebook, Inc. and its affiliates.
--
-- This source code is licensed under the MIT license found in the
-- LICENSE file in the root directory of this source tree.
--
{-# LANGUAGE CPP #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
module Retrie.Rewrites.Patterns (patternSynonymsToRewrites) where

import Control.Monad.State (StateT(runStateT))
import Control.Monad
import Data.Maybe

import Retrie.ExactPrint
import Retrie.Expr
import Retrie.GHC
import Retrie.Quantifiers
import Retrie.Rewrites.Function
import Retrie.Types
import Retrie.Universe
import Retrie.Util

patternSynonymsToRewrites
  :: [(FastString, Direction)]
  -> AnnotatedModule
#if __GLASGOW_HASKELL__ < 900
  -> IO (UniqFM [Rewrite Universe])
#else
  -> IO (UniqFM FastString [Rewrite Universe])
#endif
patternSynonymsToRewrites specs am = fmap astA $ transformA am $ \(L _ m) -> do
  let
    fsMap = uniqBag specs
  imports <- getImports RightToLeft (hsmodName m)
  rrs <- sequence
      [ do
          patRewrite <- mkPatRewrite dir imports nm params lrhs
          expRewrites <- mkExpRewrite dir imports nm params rhs patdir
          return (rdr, toURewrite patRewrite : map toURewrite expRewrites)
      | L _ (ValD _ (PatSynBind _ (PSB _ nm params rhs patdir))) <- hsmodDecls m
      , let rdr = rdrFS (unLoc nm)
      , dir <- fromMaybe [] (lookupUFM fsMap rdr)
      , Just lrhs <- [dLPat rhs]
      ]

  return $ listToUFM_C (++) rrs

mkPatRewrite
  :: Direction
  -> AnnotatedImports
  -> LRdrName
  -> HsConDetails LRdrName [RecordPatSynField LRdrName]
  -> Located (Pat GhcPs)
  -> TransformT IO (Rewrite (Located (Pat GhcPs)))
mkPatRewrite dir imports patName params rhs = do
  lhs <- asPat patName params

  (pat, temp) <- case dir of
    LeftToRight -> return (lhs, rhs)
    RightToLeft -> do
      setEntryDPT lhs (DP (0,0))
      -- Patterns from lhs have wonky annotations,
      -- the space will be attached to the name, not to the ConPatIn ast node
      setEntryDPTunderConPatIn lhs (DP (0,0))
      return (rhs, lhs)

  p <- pruneA pat
  t <- pruneA temp
  let bs = collectPatBinders (cLPat temp)
  return $ addRewriteImports imports $ mkRewrite (mkQs bs) p t

  where
    setEntryDPTunderConPatIn
      :: Monad m => Located (Pat GhcPs) -> DeltaPos -> TransformT m ()
#if __GLASGOW_HASKELL__ < 900
    setEntryDPTunderConPatIn (L _ (ConPatIn nm _)) = setEntryDPT nm
#else
    setEntryDPTunderConPatIn (L _ (ConPat _ nm _)) = setEntryDPT nm
#endif
    setEntryDPTunderConPatIn _ = const $ return ()

asPat
  :: Monad m
  => LRdrName
  -> HsConDetails LRdrName [RecordPatSynField LRdrName]
  -> TransformT m (Located (Pat GhcPs))
asPat patName params = do
  params' <- bitraverseHsConDetails mkVarPat convertFields params
  mkConPatIn patName params'
  where

    convertFields fields =
      HsRecFields <$> traverse convertField fields <*> pure Nothing

    convertField RecordPatSynField{..} = do
      hsRecFieldLbl <- mkLoc $ mkFieldOcc recordPatSynSelectorId
      hsRecFieldArg <- mkVarPat recordPatSynPatVar
      let hsRecPun = False
      mkLoc HsRecField{..}


mkExpRewrite
  :: Direction
  -> AnnotatedImports
  -> LRdrName
  -> HsConDetails LRdrName [RecordPatSynField LRdrName]
  -> LPat GhcPs
  -> HsPatSynDir GhcPs
  -> TransformT IO [Rewrite (LHsExpr GhcPs)]
mkExpRewrite dir imports patName params rhs patDir = do
  fe <- mkLocatedHsVar patName
  let altsFromParams = case params of
        PrefixCon names -> buildMatch names rhs
        InfixCon a1 a2 -> buildMatch [a1, a2] rhs
        RecCon{} -> missingSyntax "RecCon"
  alts <- case patDir of
    ExplicitBidirectional MG{mg_alts} -> pure $ unLoc mg_alts
    ImplicitBidirectional -> altsFromParams
    _ -> pure []
  fmap concat $ forM alts $ matchToRewrites fe imports dir

buildMatch
  :: Monad m
  => [Located (IdP GhcPs)]
  -> LPat GhcPs
  -> TransformT m [LMatch GhcPs (LHsExpr GhcPs)]
buildMatch names rhs = do
  pats <- traverse mkVarPat names
  let bs = collectPatBinders rhs
  (rhsExpr,(_,_bs')) <- runStateT (patToExpr rhs) (wildSupply bs, bs)
  let alt = mkMatch PatSyn pats rhsExpr (noLoc emptyLocalBinds)
  return [alt]