module Language.Haskell.Inspector (
  hasComposition,
  hasGuards,
  hasIf,
  hasConditional,
  hasLambda,
  hasDirectRecursion,
  hasUsage,
  hasComprehension,
  hasBinding,
  hasTypeDeclaration,
  hasTypeSignature,
  hasExpression,
  hasDecl,
  hasRhs,
  isParseable,
  Inspection,
  GlobalInspection
  ) where

import  Language.Haskell.Syntax
import  Language.Haskell.Names (isName)
import  Language.Haskell.Explorer

type Inspection = Binding -> Code  -> Bool
type GlobalInspection = Code  -> Bool

-- | Inspection that tells whether a binding uses the composition operator '.'
-- in its definition
hasComposition :: Inspection
hasComposition = hasExpression f
  where f (O (HsQVarOp (UnQual (HsSymbol ".")))) = True
        f _ = False

-- | Inspection that tells whether a binding uses guards
-- in its definition
hasGuards :: Inspection
hasGuards = hasRhs f
  where f (HsGuardedRhss _) = True
        f _ = False

-- | Inspection that tells whether a binding uses ifs
-- in its definition
hasIf :: Inspection
hasIf = hasExpression f
  where f (E (HsIf _ _ _)) = True
        f _ = False

-- | Inspection that tells whether a binding uses ifs or guards
-- in its definition
hasConditional :: Inspection
hasConditional target code = hasIf target code || hasGuards target code

-- | Inspection that tells whether a binding uses a lambda expression
-- in its definition
hasLambda :: Inspection
hasLambda = hasExpression f
  where f (E (HsLambda _ _ _)) = True
        f _ = False


-- | Inspection that tells whether a binding is direct recursive
hasDirectRecursion :: Inspection
hasDirectRecursion binding = hasUsage binding binding

-- | Inspection that tells whether a binding uses the the given target binding
-- in its definition
hasUsage :: String -> Inspection
hasUsage target = hasExpression f
  where f expr | (Just n) <- expressionToBinding expr = n == target
               | otherwise = False

-- | Inspection that tells whether a binding uses lists comprehensions
-- in its definition
hasComprehension :: Inspection
hasComprehension = hasExpression f
  where f (E (HsListComp _ _)) = True
        f _ = False

-- | Inspection that tells whether a top level binding exists
hasBinding :: Inspection
hasBinding binding = not.null.rhssOf binding

hasTypeDeclaration :: Inspection
hasTypeDeclaration binding = hasDecl f
  where f (HsTypeDecl _ hsName _ _) = isName binding hsName
        f _                         = False

hasTypeSignature :: Inspection
hasTypeSignature binding = hasDecl f
  where f (HsTypeSig _ [hsName] _)  = isName binding hsName
        f _                         = False

hasExpression :: (EO -> Bool) -> Inspection
hasExpression f binding = has f (expressionsOf binding)

hasRhs :: (HsRhs -> Bool)-> Inspection
hasRhs f binding = has f (rhssOf binding)

isParseable :: GlobalInspection
isParseable = not.null.parseDecls

hasDecl :: (HsDecl -> Bool) -> GlobalInspection
hasDecl f = has f parseDecls


-- private

has f g = any f . g