packages feed

mulang-3.5.2: src/Language/Mulang/Inspector/Generic.hs

module Language.Mulang.Inspector.Generic (
  parses,
  assigns,
  calls,
  uses,
  usesIf,
  declares,
  declaresVariable,
  declaresRecursively,
  declaresEntryPoint,
  declaresFunction,
  declaresComputation,
  declaresComputationWithArity,
  declaresComputationWithArity',
  declaresTypeAlias,
  declaresTypeSignature,
  usesAnonymousVariable,
  raises,
  rescues,
  usesExceptions,
  usesExceptionHandling,
  containsExpression,
  containsDeclaration,
  containsBoundDeclaration,
  containsBody,
  matchesType,
  Inspection,
  IdentifierInspection) where

import Language.Mulang.Ast
import Language.Mulang.Identifier
import Language.Mulang.Generator (expressions, boundDeclarations, equationBodies, declarations, referencedIdentifiers)

import Data.Maybe (listToMaybe)

type Inspection = Expression  -> Bool
type IdentifierInspection = IdentifierPredicate -> Inspection

-- | Inspection that tells whether an expression is equal to a given piece of code after being parsed
parses :: (String -> Expression) -> String -> Inspection
parses parser code = (== (parser code))

assigns :: IdentifierInspection
assigns predicate = containsExpression f
  where f (Assignment name _)  = predicate name
        f (Variable name _)    = predicate name
        f (Attribute name _)   = predicate name
        f _                    = False

-- | Inspection that tells whether an expression uses the the given target identifier
-- in its definition
uses :: IdentifierInspection
uses p = containsExpression f
  where f = any p . referencedIdentifiers

calls :: IdentifierInspection
calls p = containsExpression f
  where f (Call (Reference id) _ ) = p id
        f _                        = False


-- | Inspection that tells whether an expression uses ifs
-- in its definition
usesIf :: Inspection
usesIf = containsExpression f
  where f (If _ _ _) = True
        f _          = False

-- | Inspection that tells whether a top level declaration exists
declares :: IdentifierInspection
declares = containsBoundDeclaration f
  where f (TypeSignature _ _ _) = False
        f _                     = True

-- | Inspection that tells whether an expression is direct recursive
declaresRecursively :: IdentifierInspection
declaresRecursively = containsBoundDeclaration f
  where f e | (Just name) <- (nameOf e) = uses (named name) e
            | otherwise = False

        nameOf :: Expression -> Maybe Identifier
        nameOf = fmap fst . listToMaybe . declarations


declaresFunction :: IdentifierInspection
declaresFunction = containsBoundDeclaration f
  where f (Function _ _) = True
        f _              = False

declaresVariable :: IdentifierInspection
declaresVariable = containsBoundDeclaration f
  where f (Variable _ _)  = True
        f _               = False

declaresEntryPoint :: IdentifierInspection
declaresEntryPoint = containsBoundDeclaration f
  where f (EntryPoint _ _)  = True
        f _                 = False

-- | Inspection that tells whether a top level computation declaration exists
declaresComputation :: IdentifierInspection
declaresComputation = declaresComputationWithArity' (const True)

declaresComputationWithArity :: Int -> IdentifierInspection
declaresComputationWithArity arity = declaresComputationWithArity' (== arity)

declaresComputationWithArity' :: (Int -> Bool) -> IdentifierInspection
declaresComputationWithArity' arityPredicate = containsBoundDeclaration f
  where f (Subroutine _ es)       = any equationArityIs es
        f (Clause _ args _)       = argsHaveArity args
        f _  = False

        equationArityIs (Equation args _) = argsHaveArity args

        argsHaveArity = arityPredicate.length

declaresTypeAlias :: IdentifierInspection
declaresTypeAlias = containsBoundDeclaration f
  where f (TypeAlias _) = True
        f _             = False

declaresTypeSignature :: IdentifierInspection
declaresTypeSignature = containsBoundDeclaration f
  where f (TypeSignature _ _ _) = True
        f _                     = False

raises :: IdentifierInspection
raises predicate = containsExpression f
  where f (Raise (New n _))     = predicate n
        f (Raise (Reference n)) = predicate n
        f _                     = False

usesExceptions :: Inspection
usesExceptions = containsExpression f
  where f (Raise _)     = True
        f _             = False

rescues :: IdentifierInspection
rescues predicate = containsExpression f
  where f (Try _ rescues _) = any (matchesType predicate) . map fst  $ rescues
        f _                 = False

usesExceptionHandling :: Inspection
usesExceptionHandling  = containsExpression f
  where f (Try _ _ _) = True
        f _           = False

usesAnonymousVariable :: Inspection
usesAnonymousVariable = containsExpression f
  where f (Subroutine _ equations)    = equationContainsWildcard equations
--TODO        f (Lambda args _)                      = equationContainsWildcard equations
        f (Clause _ params _)         = paramsContainsWildcard params
        f _                           = False

        equationContainsWildcard = any (paramsContainsWildcard . equationParams)
        paramsContainsWildcard = any isOrContainsWildcard

        isOrContainsWildcard (InfixApplicationPattern p1 _ p2) = any isOrContainsWildcard [p1, p2]
        isOrContainsWildcard (ApplicationPattern _ ps)         = any isOrContainsWildcard ps
        isOrContainsWildcard (TuplePattern ps)                 = any isOrContainsWildcard ps
        isOrContainsWildcard (ListPattern ps)                  = any isOrContainsWildcard ps
        isOrContainsWildcard (FunctorPattern _ ps)             = any isOrContainsWildcard ps
        isOrContainsWildcard (AsPattern _ p)                   = isOrContainsWildcard p
        isOrContainsWildcard WildcardPattern                   = True
        isOrContainsWildcard _                                 = False


containsExpression :: (Expression -> Bool) -> Inspection
containsExpression f = has f expressions

containsBody :: (EquationBody -> Bool)-> Inspection
containsBody f = has f equationBodies

containsBoundDeclaration :: (Expression -> Bool) -> IdentifierInspection
containsBoundDeclaration f b  = has f (boundDeclarations b)

containsDeclaration :: (Expression -> Bool) -> Inspection
containsDeclaration f = has f (map snd . declarations)

matchesType :: IdentifierPredicate -> Pattern -> Bool
matchesType predicate (TypePattern n)               = predicate n
matchesType predicate (AsPattern _ (TypePattern n)) = predicate n
matchesType predicate (UnionPattern patterns)       = any (matchesType predicate) patterns
matchesType _         _                             = False

-- private

has f g = any f . g