packages feed

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

{-# LANGUAGE CPP #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RecordWildCards #-}

module HIndent.Ast.Pattern
  ( Pattern
  , PatInsidePatDecl
  , mkPattern
  , mkPatInsidePatDecl
  ) where

import Control.Monad
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NE
import qualified GHC.Types.Basic as GHC
import qualified GHC.Types.SrcLoc as GHC
import {-# SOURCE #-} HIndent.Ast.Expression (Expression, mkExpression)
import HIndent.Ast.Expression.Splice
import HIndent.Ast.Name.Infix hiding (unlessSpecialOp)
import qualified HIndent.Ast.Name.Infix as InfixName
import HIndent.Ast.Name.Prefix
import HIndent.Ast.NodeComments hiding (fromEpAnn)
import HIndent.Ast.Pattern.RecordFields
import HIndent.Ast.Type
import HIndent.Ast.WithComments
import qualified HIndent.GhcLibParserWrapper.GHC.Hs as GHC
import {-# SOURCE #-} HIndent.Pretty
import HIndent.Pretty.Combinators
import HIndent.Pretty.NodeComments (CommentExtraction(..))

data Pattern
  = WildCard
  | Variable (WithComments PrefixName)
  | Lazy (WithComments Pattern)
  | As
      { name :: WithComments PrefixName
      , pat :: WithComments Pattern
      }
  | Parenthesized (WithComments Pattern)
  | Bang (WithComments Pattern)
  | List [WithComments Pattern]
  | Tuple
      { boxed :: Bool
      , patterns :: [WithComments Pattern]
      }
  | Sum
      { pat :: WithComments Pattern
      , position :: Int
      , arity :: Int
      }
  | PrefixConstructor
      { name :: WithComments PrefixName
      , patterns :: [WithComments Pattern]
      }
  | InfixConstructor
      { left :: WithComments Pattern
      , operator :: WithComments InfixName
      , right :: WithComments Pattern
      }
  | RecordConstructor
      { name :: WithComments PrefixName
      , fields :: RecordFieldsPat
      }
  | View
      { expression :: WithComments Expression
      , pat :: WithComments Pattern
      }
  | Splice (WithComments Splice)
  | Literal (GHC.HsLit GHC.GhcPs)
  | Overloaded (GHC.HsOverLit GHC.GhcPs)
  | NPlusK
      { n :: WithComments PrefixName
      , k :: GHC.HsOverLit GHC.GhcPs
      }
  | Signature
      { pat :: WithComments Pattern
      , sig :: WithComments Type
      }
  | Or (NonEmpty (WithComments Pattern))

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

instance Pretty Pattern where
  pretty' WildCard = string "_"
  pretty' (Variable name) = pretty name
  pretty' (Lazy pat) = string "~" >> pretty pat
  pretty' As {..} = pretty name >> string "@" >> pretty pat
  pretty' (Parenthesized pat) = parens $ pretty pat
  pretty' (Bang pat) = string "!" >> pretty pat
  pretty' (List pats) = hList $ pretty <$> pats
  pretty' Tuple {boxed = True, ..} = hTuple $ pretty <$> patterns
  pretty' Tuple {boxed = False, ..} = hUnboxedTuple $ pretty <$> patterns
  pretty' Sum {..} = do
    string "(#"
    forM_ [1 .. arity] $ \idx -> do
      if idx == position
        then string " " >> pretty pat >> string " "
        else string " "
      when (idx < arity) $ string "|"
    string "#)"
  pretty' PrefixConstructor {..} = do
    pretty name
    spacePrefixed $ pretty <$> patterns
  pretty' InfixConstructor {..} = do
    pretty left
    InfixName.unlessSpecialOp (getNode operator) space
    pretty operator
    InfixName.unlessSpecialOp (getNode operator) space
    pretty right
  pretty' RecordConstructor {..} = (pretty name >> space) |=> pretty fields
  pretty' View {..} = spaced [pretty expression, string "->", pretty pat]
  pretty' (Splice splice) = pretty splice
  pretty' (Literal lit) = pretty lit
  pretty' (Overloaded lit) = pretty lit
  pretty' NPlusK {..} = pretty n >> string "+" >> pretty k
  pretty' Signature {..} = spaced [pretty pat, string "::", pretty sig]
  pretty' (Or pats) = inter (string "; ") $ pretty <$> NE.toList pats

mkPattern :: GHC.Pat GHC.GhcPs -> Pattern
mkPattern GHC.WildPat {} = WildCard
mkPattern (GHC.VarPat _ x) = Variable $ mkPrefixName <$> fromGenLocated x
#if MIN_VERSION_ghc_lib_parser(9, 10, 1)
mkPattern GHC.EmbTyPat {} = notGeneratedByParser
mkPattern GHC.InvisPat {} = notGeneratedByParser
#endif
mkPattern (GHC.LazyPat _ x) = Lazy $ mkPattern <$> fromGenLocated x
#if MIN_VERSION_ghc_lib_parser(9, 6, 1) && !MIN_VERSION_ghc_lib_parser(9, 10, 1)
mkPattern (GHC.AsPat _ a _ b) =
  As
    { name = mkPrefixName <$> fromGenLocated a
    , pat = mkPattern <$> fromGenLocated b
    }
#else
mkPattern (GHC.AsPat _ a b) =
  As
    { name = mkPrefixName <$> fromGenLocated a
    , pat = mkPattern <$> fromGenLocated b
    }
#endif
#if MIN_VERSION_ghc_lib_parser(9, 4, 1) && !MIN_VERSION_ghc_lib_parser(9, 10, 1)
mkPattern (GHC.ParPat _ _ inner _) =
  Parenthesized $ mkPattern <$> fromGenLocated inner
#else
mkPattern (GHC.ParPat _ inner) =
  Parenthesized $ mkPattern <$> fromGenLocated inner
#endif
mkPattern (GHC.BangPat _ x) = Bang $ mkPattern <$> fromGenLocated x
mkPattern (GHC.ListPat _ xs) = List $ fmap (fmap mkPattern . fromGenLocated) xs
mkPattern (GHC.TuplePat _ pats GHC.Boxed) =
  Tuple {boxed = True, patterns = fmap (fmap mkPattern . fromGenLocated) pats}
mkPattern (GHC.TuplePat _ pats GHC.Unboxed) =
  Tuple {boxed = False, patterns = fmap (fmap mkPattern . fromGenLocated) pats}
mkPattern (GHC.SumPat _ x position numElem) =
  Sum
    {pat = mkPattern <$> fromGenLocated x, position = position, arity = numElem}
mkPattern GHC.ConPat {..} =
  case pat_args of
    GHC.PrefixCon _ as ->
      PrefixConstructor
        { name = mkPrefixName <$> fromGenLocated pat_con
        , patterns = fmap (fmap mkPattern . fromGenLocated) as
        }
    GHC.RecCon rec ->
      RecordConstructor
        { name = mkPrefixName <$> fromGenLocated pat_con
        , fields = mkRecordFieldsPat rec
        }
    GHC.InfixCon a b ->
      InfixConstructor
        { left = mkPattern <$> fromGenLocated a
        , operator = mkInfixName <$> fromGenLocated pat_con
        , right = mkPattern <$> fromGenLocated b
        }
mkPattern (GHC.ViewPat _ l r) =
  View
    { expression = mkExpression <$> fromGenLocated l
    , pat = mkPattern <$> fromGenLocated r
    }
mkPattern (GHC.SplicePat _ x) = Splice $ mkWithComments $ mkSplice x
mkPattern (GHC.LitPat _ x) = Literal x
mkPattern (GHC.NPat _ x _ _) = Overloaded $ GHC.unLoc x
mkPattern (GHC.NPlusKPat _ n k _ _ _) =
  NPlusK {n = mkPrefixName <$> fromGenLocated n, k = GHC.unLoc k}
mkPattern (GHC.SigPat _ l r) =
  Signature
    { pat = mkPattern <$> fromGenLocated l
    , sig =
        flattenComments
          $ fmap mkType
              <$> fromEpAnn (GHC.hsps_ext r) (fromGenLocated $ GHC.hsps_body r)
    }
#if MIN_VERSION_ghc_lib_parser(9, 12, 1)
mkPattern (GHC.OrPat _ pats) = Or $ fmap (fmap mkPattern . fromGenLocated) pats
#endif
newtype PatInsidePatDecl =
  PatInsidePatDecl Pattern

instance CommentExtraction PatInsidePatDecl where
  nodeComments (PatInsidePatDecl p) = nodeComments p

instance Pretty PatInsidePatDecl where
  pretty' (PatInsidePatDecl InfixConstructor {..}) =
    spaced [pretty left, pretty operator, pretty right]
  pretty' (PatInsidePatDecl p) = pretty p

mkPatInsidePatDecl :: GHC.Pat GHC.GhcPs -> PatInsidePatDecl
mkPatInsidePatDecl = PatInsidePatDecl . mkPattern
#if MIN_VERSION_ghc_lib_parser(9, 10, 1)
notGeneratedByParser :: a
notGeneratedByParser = error "This AST node is not generated by the parser."
#endif