{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE ScopedTypeVariables        #-}
-- | Class for defining code metrics, and its simplest implementation - number of lines of code.
module Language.Haskell.Homplexity.Metric (
    Metric (..)
  , LOC
  , locT
  , measureAs
  , measureFor
  ) where

import           Data.Data
import           Data.Function
--import Data.Functor
import           Control.Arrow
import           Data.Generics.Uniplate.Data
import           Data.List
import           Language.Haskell.Exts.SrcLoc
--import Language.Haskell.Exts.Syntax

import           Language.Haskell.Homplexity.CodeFragment

-- | Metric can be computed on a set of @CodeFragment@ fragments
-- and then shown.
class (CodeFragment c, Show m) => Metric m c where
  measure :: c -> m

-- | Number of lines of code
-- (example metric)
newtype LOC = LOC { LOC -> Int
_asInt :: Int }
  deriving (Eq LOC
Eq LOC
-> (LOC -> LOC -> Ordering)
-> (LOC -> LOC -> Bool)
-> (LOC -> LOC -> Bool)
-> (LOC -> LOC -> Bool)
-> (LOC -> LOC -> Bool)
-> (LOC -> LOC -> LOC)
-> (LOC -> LOC -> LOC)
-> Ord LOC
LOC -> LOC -> Bool
LOC -> LOC -> Ordering
LOC -> LOC -> LOC
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: LOC -> LOC -> Ordering
compare :: LOC -> LOC -> Ordering
$c< :: LOC -> LOC -> Bool
< :: LOC -> LOC -> Bool
$c<= :: LOC -> LOC -> Bool
<= :: LOC -> LOC -> Bool
$c> :: LOC -> LOC -> Bool
> :: LOC -> LOC -> Bool
$c>= :: LOC -> LOC -> Bool
>= :: LOC -> LOC -> Bool
$cmax :: LOC -> LOC -> LOC
max :: LOC -> LOC -> LOC
$cmin :: LOC -> LOC -> LOC
min :: LOC -> LOC -> LOC
Ord, LOC -> LOC -> Bool
(LOC -> LOC -> Bool) -> (LOC -> LOC -> Bool) -> Eq LOC
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: LOC -> LOC -> Bool
== :: LOC -> LOC -> Bool
$c/= :: LOC -> LOC -> Bool
/= :: LOC -> LOC -> Bool
Eq, Int -> LOC
LOC -> Int
LOC -> [LOC]
LOC -> LOC
LOC -> LOC -> [LOC]
LOC -> LOC -> LOC -> [LOC]
(LOC -> LOC)
-> (LOC -> LOC)
-> (Int -> LOC)
-> (LOC -> Int)
-> (LOC -> [LOC])
-> (LOC -> LOC -> [LOC])
-> (LOC -> LOC -> [LOC])
-> (LOC -> LOC -> LOC -> [LOC])
-> Enum LOC
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: LOC -> LOC
succ :: LOC -> LOC
$cpred :: LOC -> LOC
pred :: LOC -> LOC
$ctoEnum :: Int -> LOC
toEnum :: Int -> LOC
$cfromEnum :: LOC -> Int
fromEnum :: LOC -> Int
$cenumFrom :: LOC -> [LOC]
enumFrom :: LOC -> [LOC]
$cenumFromThen :: LOC -> LOC -> [LOC]
enumFromThen :: LOC -> LOC -> [LOC]
$cenumFromTo :: LOC -> LOC -> [LOC]
enumFromTo :: LOC -> LOC -> [LOC]
$cenumFromThenTo :: LOC -> LOC -> LOC -> [LOC]
enumFromThenTo :: LOC -> LOC -> LOC -> [LOC]
Enum, Integer -> LOC
LOC -> LOC
LOC -> LOC -> LOC
(LOC -> LOC -> LOC)
-> (LOC -> LOC -> LOC)
-> (LOC -> LOC -> LOC)
-> (LOC -> LOC)
-> (LOC -> LOC)
-> (LOC -> LOC)
-> (Integer -> LOC)
-> Num LOC
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
$c+ :: LOC -> LOC -> LOC
+ :: LOC -> LOC -> LOC
$c- :: LOC -> LOC -> LOC
- :: LOC -> LOC -> LOC
$c* :: LOC -> LOC -> LOC
* :: LOC -> LOC -> LOC
$cnegate :: LOC -> LOC
negate :: LOC -> LOC
$cabs :: LOC -> LOC
abs :: LOC -> LOC
$csignum :: LOC -> LOC
signum :: LOC -> LOC
$cfromInteger :: Integer -> LOC
fromInteger :: Integer -> LOC
Num, Num LOC
Ord LOC
Num LOC -> Ord LOC -> (LOC -> Rational) -> Real LOC
LOC -> Rational
forall a. Num a -> Ord a -> (a -> Rational) -> Real a
$ctoRational :: LOC -> Rational
toRational :: LOC -> Rational
Real, Enum LOC
Real LOC
Real LOC
-> Enum LOC
-> (LOC -> LOC -> LOC)
-> (LOC -> LOC -> LOC)
-> (LOC -> LOC -> LOC)
-> (LOC -> LOC -> LOC)
-> (LOC -> LOC -> (LOC, LOC))
-> (LOC -> LOC -> (LOC, LOC))
-> (LOC -> Integer)
-> Integral LOC
LOC -> Integer
LOC -> LOC -> (LOC, LOC)
LOC -> LOC -> LOC
forall a.
Real a
-> Enum a
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> (a, a))
-> (a -> a -> (a, a))
-> (a -> Integer)
-> Integral a
$cquot :: LOC -> LOC -> LOC
quot :: LOC -> LOC -> LOC
$crem :: LOC -> LOC -> LOC
rem :: LOC -> LOC -> LOC
$cdiv :: LOC -> LOC -> LOC
div :: LOC -> LOC -> LOC
$cmod :: LOC -> LOC -> LOC
mod :: LOC -> LOC -> LOC
$cquotRem :: LOC -> LOC -> (LOC, LOC)
quotRem :: LOC -> LOC -> (LOC, LOC)
$cdivMod :: LOC -> LOC -> (LOC, LOC)
divMod :: LOC -> LOC -> (LOC, LOC)
$ctoInteger :: LOC -> Integer
toInteger :: LOC -> Integer
Integral)

-- | Proxy for passing @LOC@ type as parameter.
locT :: Proxy LOC
locT :: Proxy LOC
locT  = Proxy LOC
forall {k} (t :: k). Proxy t
Proxy

instance Show LOC where
  showsPrec :: Int -> LOC -> ShowS
showsPrec Int
_ (LOC Int
l) = Int -> ShowS
forall a. Show a => a -> ShowS
shows Int
l ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
" lines of code"String -> ShowS
forall a. [a] -> [a] -> [a]
++)

instance Read LOC where
  readsPrec :: Int -> ReadS LOC
readsPrec Int
prec String
str = (Int -> LOC) -> (Int, String) -> (LOC, String)
forall b c d. (b -> c) -> (b, d) -> (c, d)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (b, d) (c, d)
first Int -> LOC
LOC ((Int, String) -> (LOC, String))
-> [(Int, String)] -> [(LOC, String)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> ReadS Int
forall a. Read a => Int -> ReadS a
readsPrec Int
prec String
str

instance (CodeFragment c) => Metric LOC c where
  measure :: c -> LOC
measure = Int -> LOC
LOC
          (Int -> LOC) -> (c -> Int) -> c -> LOC
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Int] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length                          -- total number of lines that contain at least one object with SrcLoc
          ([Int] -> Int) -> (c -> [Int]) -> c -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([SrcLoc] -> [Int]) -> [[SrcLoc]] -> [Int]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ([Int] -> [Int]
forall a. Eq a => [a] -> [a]
nub ([Int] -> [Int]) -> ([SrcLoc] -> [Int]) -> [SrcLoc] -> [Int]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SrcLoc -> Int) -> [SrcLoc] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map SrcLoc -> Int
srcLine)   -- remove duplicate lines within the same file
          ([[SrcLoc]] -> [Int]) -> (c -> [[SrcLoc]]) -> c -> [Int]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SrcLoc -> SrcLoc -> Bool) -> [SrcLoc] -> [[SrcLoc]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupBy (String -> String -> Bool
forall a. Eq a => a -> a -> Bool
(==) (String -> String -> Bool)
-> (SrcLoc -> String) -> SrcLoc -> SrcLoc -> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` SrcLoc -> String
srcFilename) -- group by filename
          ([SrcLoc] -> [[SrcLoc]]) -> (c -> [SrcLoc]) -> c -> [[SrcLoc]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. c -> [SrcLoc]
forall from to. Biplate from to => from -> [to]
universeBi                      -- all SrcLoc objects

-- | Convenience function for fixing the @Metric@ type.
measureAs :: (Metric m c) => Proxy m -> c -> m
measureAs :: forall m c. Metric m c => Proxy m -> c -> m
measureAs Proxy m
_ = c -> m
forall m c. Metric m c => c -> m
measure

-- | Convenience function for fixing both the @Metric@ and @CodeFragment@ for which the metric is computed.
measureFor :: (Metric m c) => Proxy m -> Proxy c -> c -> m
measureFor :: forall m c. Metric m c => Proxy m -> Proxy c -> c -> m
measureFor Proxy m
_ Proxy c
_ = c -> m
forall m c. Metric m c => c -> m
measure