packages feed

hindent-6.3.0: src/HIndent/Ast/Match.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}

module HIndent.Ast.Match
  ( Match
  , mkExprMatch
  , mkCmdMatch
  ) where

import Control.Monad (unless, when)
import qualified GHC.Hs as GHC
import qualified GHC.Types.Fixity as Fixity
#if MIN_VERSION_ghc_lib_parser(9, 10, 1)
import qualified GHC.Types.Name.Reader as GHC
#endif
import qualified GHC.Types.SrcLoc as GHC
import HIndent.Applicative (whenJust)
import HIndent.Ast.Declaration.Bind.GuardedRhs
  ( GuardedRhs
  , mkCaseCmdGuardedRhs
  , mkCaseGuardedRhs
  , mkGuardedRhs
  , mkLambdaCmdGuardedRhs
  , mkLambdaGuardedRhs
  )
import HIndent.Ast.Name.Infix (InfixName, mkInfixName)
import HIndent.Ast.Name.Prefix (PrefixName, mkPrefixName)
import HIndent.Ast.NodeComments
import HIndent.Ast.Pattern (Pattern, mkPattern)
import HIndent.Ast.Type.Strictness (Strictness, mkStrictness)
#if MIN_VERSION_ghc_lib_parser(9, 12, 1)
import HIndent.Ast.WithComments (WithComments, fromGenLocated, prettyWith)
#else
import HIndent.Ast.WithComments
  ( WithComments
  , fromGenLocated
  , mkWithComments
  , prettyWith
  )
#endif
import {-# SOURCE #-} HIndent.Pretty (Pretty(..), pretty)
import HIndent.Pretty.Combinators
import HIndent.Pretty.NodeComments (CommentExtraction(..))

data InfixOperands = InfixOperands
  { left :: WithComments Pattern
  , operator :: WithComments InfixName
  , right :: WithComments Pattern
  , rest :: [WithComments Pattern]
  }

instance CommentExtraction InfixOperands where
  nodeComments _ = NodeComments [] [] []

instance Pretty InfixOperands where
  pretty' InfixOperands {..} =
    spaced $ pretty left : pretty operator : pretty right : fmap pretty rest

data Match
  = Lambda
      { needsSpaceAfterLambda :: Bool
      , patterns :: WithComments [WithComments Pattern]
      , rhs :: GuardedRhs
      }
  | Case
      { patterns :: WithComments [WithComments Pattern]
      , rhs :: GuardedRhs
      }
  | FunctionPrefix
      { strictness :: Maybe Strictness
      , name :: WithComments PrefixName
      , patterns :: WithComments [WithComments Pattern]
      , rhs :: GuardedRhs
      }
  | FunctionInfix
      { operands :: WithComments InfixOperands
      , rhs :: GuardedRhs
      }

instance CommentExtraction Match where
  nodeComments _ = NodeComments [] [] []

instance Pretty Match where
  pretty' Lambda {..} = do
    string "\\"
    when needsSpaceAfterLambda space
    prettyWith patterns $ spaced . fmap pretty
    pretty rhs
  pretty' Case {..} = do
    prettyWith patterns $ spaced . fmap pretty
    pretty rhs
  pretty' FunctionPrefix {..} = do
    whenJust strictness pretty
    pretty name
    prettyWith patterns $ \pats ->
      unless (null pats) $ spacePrefixed $ fmap pretty pats
    pretty rhs
  pretty' FunctionInfix {..} = do
    pretty operands
    pretty rhs
#if MIN_VERSION_ghc_lib_parser(9, 12, 1)
mkExprMatch :: GHC.Match GHC.GhcPs (GHC.LHsExpr GHC.GhcPs) -> Match
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt GHC.LamSingle, ..} =
  Lambda
    { needsSpaceAfterLambda = lambdaNeedsSpace $ GHC.unLoc m_pats
    , patterns =
        fmap (fmap (fromGenLocated . fmap mkPattern)) $ fromGenLocated $ m_pats
    , rhs = mkLambdaGuardedRhs m_grhss
    }
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt _, ..} =
  Case
    { rhs = mkCaseGuardedRhs m_grhss
    , patterns =
        fmap (fmap (fromGenLocated . fmap mkPattern)) $ fromGenLocated $ m_pats
    }
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.CaseAlt, ..} =
  Case
    { rhs = mkCaseGuardedRhs m_grhss
    , patterns =
        fmap (fmap (fromGenLocated . fmap mkPattern)) $ fromGenLocated $ m_pats
    }
mkExprMatch GHC.Match {GHC.m_ctxt = ctxt@GHC.FunRhs {}, ..} =
  mkFunctionMatch ctxt patterns (mkGuardedRhs m_grhss)
  where
    patterns =
      fmap (fmap (fromGenLocated . fmap mkPattern)) $ fromGenLocated $ m_pats
mkExprMatch _ = error "`ghc-lib-parser` never generates this AST node."
#elif MIN_VERSION_ghc_lib_parser(9, 10, 1)
mkExprMatch :: GHC.Match GHC.GhcPs (GHC.LHsExpr GHC.GhcPs) -> Match
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt GHC.LamSingle, ..} =
  Lambda
    { needsSpaceAfterLambda = lambdaNeedsSpace m_pats
    , patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
    , rhs = mkLambdaGuardedRhs m_grhss
    }
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt GHC.LamCase, ..} =
  Case
    { rhs = mkCaseGuardedRhs m_grhss
    , patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
    }
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt GHC.LamCases, ..} =
  Case
    { patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
    , rhs = mkCaseGuardedRhs m_grhss
    }
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.CaseAlt, ..} =
  Case
    { patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
    , rhs = mkCaseGuardedRhs m_grhss
    }
mkExprMatch GHC.Match {GHC.m_ctxt = ctxt@GHC.FunRhs {}, ..} =
  mkFunctionMatch ctxt patterns (mkGuardedRhs m_grhss)
  where
    patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
mkExprMatch _ = error "`ghc-lib-parser` never generates this AST node."
#elif MIN_VERSION_ghc_lib_parser(9, 4, 1)
mkExprMatch :: GHC.Match GHC.GhcPs (GHC.LHsExpr GHC.GhcPs) -> Match
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.LambdaExpr, ..} =
  Lambda
    { needsSpaceAfterLambda = lambdaNeedsSpace m_pats
    , patterns = patterns
    , rhs = mkLambdaGuardedRhs m_grhss
    }
  where
    patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.LamCaseAlt {}, ..} =
  Case
    { patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
    , rhs = mkCaseGuardedRhs m_grhss
    }
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.CaseAlt, ..} =
  Case
    { patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
    , rhs = mkCaseGuardedRhs m_grhss
    }
mkExprMatch GHC.Match {GHC.m_ctxt = ctxt@GHC.FunRhs {}, ..} =
  mkFunctionMatch ctxt patterns (mkGuardedRhs m_grhss)
  where
    patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
mkExprMatch _ = error "`ghc-lib-parser` never generates this AST node."
#else
mkExprMatch :: GHC.Match GHC.GhcPs (GHC.LHsExpr GHC.GhcPs) -> Match
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.LambdaExpr, ..} =
  Lambda
    { needsSpaceAfterLambda = lambdaNeedsSpace m_pats
    , patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
    , rhs = mkLambdaGuardedRhs m_grhss
    }
mkExprMatch GHC.Match {GHC.m_ctxt = GHC.CaseAlt, ..} =
  Case
    { patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
    , rhs = mkCaseGuardedRhs m_grhss
    }
mkExprMatch GHC.Match {GHC.m_ctxt = ctxt@GHC.FunRhs {}, ..} =
  mkFunctionMatch ctxt patterns (mkGuardedRhs m_grhss)
  where
    patterns = mkWithComments $ fmap (fmap mkPattern . fromGenLocated) m_pats
mkExprMatch _ = error "`ghc-lib-parser` never generates this AST node."
#endif

#if MIN_VERSION_ghc_lib_parser(9, 12, 1)
mkCmdMatch :: GHC.Match GHC.GhcPs (GHC.LHsCmd GHC.GhcPs) -> Match
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt GHC.LamSingle, ..} =
  Lambda
    { needsSpaceAfterLambda = lambdaNeedsSpace (GHC.unLoc m_pats)
    , patterns =
        fmap (fmap (fromGenLocated . fmap mkPattern)) $ fromGenLocated $ m_pats
    , rhs = mkLambdaCmdGuardedRhs m_grhss
    }
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt _, ..} =
  Case
    { patterns =
        fmap (fmap (fromGenLocated . fmap mkPattern)) $ fromGenLocated $ m_pats
    , rhs = mkCaseCmdGuardedRhs m_grhss
    }
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.CaseAlt, ..} =
  Case
    { patterns =
        fmap (fmap (fromGenLocated . fmap mkPattern)) $ fromGenLocated $ m_pats
    , rhs = mkCaseCmdGuardedRhs m_grhss
    }
mkCmdMatch _ = error "`ghc-lib-parser` never generates this AST node."
#elif MIN_VERSION_ghc_lib_parser(9, 10, 1)
mkCmdMatch :: GHC.Match GHC.GhcPs (GHC.LHsCmd GHC.GhcPs) -> Match
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt GHC.LamSingle, ..} =
  Lambda
    { needsSpaceAfterLambda = lambdaNeedsSpace m_pats
    , patterns = mkWithComments $ fmap mkPattern . fromGenLocated <$> m_pats
    , rhs = mkLambdaCmdGuardedRhs m_grhss
    }
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt GHC.LamCase, ..} =
  Case
    { patterns = mkWithComments $ fmap mkPattern . fromGenLocated <$> m_pats
    , rhs = mkCaseCmdGuardedRhs m_grhss
    }
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.LamAlt GHC.LamCases, ..} =
  Case
    { patterns = mkWithComments $ fmap mkPattern . fromGenLocated <$> m_pats
    , rhs = mkCaseCmdGuardedRhs m_grhss
    }
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.CaseAlt, ..} =
  Case
    { patterns = mkWithComments $ fmap mkPattern . fromGenLocated <$> m_pats
    , rhs = mkCaseCmdGuardedRhs m_grhss
    }
mkCmdMatch _ = error "`ghc-lib-parser` never generates this AST node."
#elif MIN_VERSION_ghc_lib_parser(9, 4, 1)
mkCmdMatch :: GHC.Match GHC.GhcPs (GHC.LHsCmd GHC.GhcPs) -> Match
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.LambdaExpr, ..} =
  Lambda
    { needsSpaceAfterLambda = lambdaNeedsSpace m_pats
    , patterns = mkWithComments $ fmap mkPattern . fromGenLocated <$> m_pats
    , rhs = mkLambdaCmdGuardedRhs m_grhss
    }
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.LamCaseAlt {}, ..} =
  Case
    { patterns = mkWithComments $ fmap mkPattern . fromGenLocated <$> m_pats
    , rhs = mkCaseCmdGuardedRhs m_grhss
    }
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.CaseAlt, ..} =
  Case
    { patterns = mkWithComments $ fmap mkPattern . fromGenLocated <$> m_pats
    , rhs = mkCaseCmdGuardedRhs m_grhss
    }
mkCmdMatch _ = error "`ghc-lib-parser` never generates this AST node."
#else
mkCmdMatch :: GHC.Match GHC.GhcPs (GHC.LHsCmd GHC.GhcPs) -> Match
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.LambdaExpr, ..} =
  Lambda
    { needsSpaceAfterLambda = lambdaNeedsSpace m_pats
    , patterns = mkWithComments $ fmap mkPattern . fromGenLocated <$> m_pats
    , rhs = mkLambdaCmdGuardedRhs m_grhss
    }
mkCmdMatch GHC.Match {GHC.m_ctxt = GHC.CaseAlt, ..} =
  Case
    { rhs = mkCaseCmdGuardedRhs m_grhss
    , patterns = mkWithComments $ fmap mkPattern . fromGenLocated <$> m_pats
    }
mkCmdMatch _ = error "`ghc-lib-parser` never generates this AST node."
#endif
#if MIN_VERSION_ghc_lib_parser(9, 10, 1)
mkFunctionMatch ::
     GHC.HsMatchContext (GHC.GenLocated GHC.SrcSpanAnnN GHC.RdrName)
  -> WithComments [WithComments Pattern]
  -> GuardedRhs
  -> Match
#else
mkFunctionMatch ::
     GHC.HsMatchContext GHC.GhcPs
  -> WithComments [WithComments Pattern]
  -> GuardedRhs
  -> Match
#endif
mkFunctionMatch GHC.FunRhs {mc_fixity = Fixity.Prefix, ..} patterns rhs =
  FunctionPrefix
    { strictness = mkStrictness mc_strictness
    , name = mkPrefixName <$> fromGenLocated mc_fun
    , ..
    }
mkFunctionMatch GHC.FunRhs {mc_fixity = Fixity.Infix, ..} pats rhs =
  FunctionInfix {..}
  where
    operands =
      flip fmap pats $ \case
        left:right:rest ->
          InfixOperands {operator = mkInfixName <$> fromGenLocated mc_fun, ..}
        _ -> error "FunctionInfix match must have at least two patterns."
mkFunctionMatch _ _ _ = error "`ghc-lib-parser` never generates this AST node."

lambdaNeedsSpace :: [GHC.LPat GHC.GhcPs] -> Bool
lambdaNeedsSpace (pat:_) =
  case GHC.unLoc pat of
    GHC.LazyPat {} -> True
    GHC.BangPat {} -> True
    _ -> False
lambdaNeedsSpace _ = False