    --------------------------------------------------------------------------------
    -- |
    -- Module      :  Text.Show.Value
    -- Copyright   :  (c) Iavor S. Diatchki 2009
    -- License     :  MIT
    --
    -- Maintainer  :  iavor.diatchki@gmail.com
    -- Stability   :  provisional
    -- Portability :  Haskell 98
    --
    -- Generic representation of Showable values.
    --------------------------------------------------------------------------------

    {-# LANGUAGE Safe #-}
    module Text.Show.Value ( Name, Value(..), hideCon ) where

    import Data.Maybe(fromMaybe,isNothing)

    -- | A name.
    type Name     = String

    -- | Generic Haskell values.
    -- 'NaN' and 'Infinity' are represented as constructors.
    -- The 'String' in the literals is the text for the literals \"as is\".
    --
    -- A chain of infix constructors means that they appeared in the input string
    -- without parentheses, i.e
    --
    -- @1 :+: 2 :*: 3@ is represented with @InfixCons 1 [(":+:",2),(":*:",3)]@, whereas
    --
    -- @1 :+: (2 :*: 3)@ is represented with @InfixCons 1 [(":+:",InfixCons 2 [(":*:",3)])]@.
    data Value    = Con Name [Value]               -- ^ Data constructor
                  | InfixCons Value [(Name,Value)] -- ^ Infix data constructor chain
                  | Rec Name [ (Name,Value) ]      -- ^ Record value
                  | Tuple [Value]                  -- ^ Tuple
                  | List [Value]                   -- ^ List
                  | Neg Value                      -- ^ Negated value
                  | Ratio Value Value              -- ^ Rational
                  | Integer String                 -- ^ Non-negative integer
                  | Float String                   -- ^ Non-negative floating num.
                  | Char String                    -- ^ Character
                  | String String                  -- ^ String
                    deriving (Eq,Show)

    {- | Hide constrcutros matching the given predicate.
    If the hidden value is in a record, we also hide
    the corresponding record field.

    If the boolean flag is true, then we also hide
    constructors all of whose fields were hidden. -}
    hideCon :: Bool -> (Name -> Bool) -> Value -> Value
    hideCon collapse hidden = toVal . delMaybe
      where
      hiddenV = Con "_" []

      toVal = fromMaybe hiddenV

      delMany vals
        | collapse && all isNothing newVals = Nothing
        | otherwise                         = Just (map toVal newVals)
        where
        newVals = map delMaybe vals

      delMaybe val =
        case val of
          Con x vs
            | hidden x  -> Nothing
            | null vs   -> Just val
            | otherwise -> Con x `fmap` delMany vs

          Rec x fs
            | hidden x  -> Nothing
            | null fs   -> Just val
            | collapse && all isNothing mbs -> Nothing
            | otherwise -> Just (Rec x [ (f,v) | (f,Just v) <- zip ls mbs ])
            where (ls,vs) = unzip fs
                  mbs     = map delMaybe vs

          InfixCons v ys
            | any hidden cs -> Nothing
            | otherwise -> do ~(v1:vs1) <- delMany (v:vs)
                              Just (InfixCons v1 (zip cs vs1))
              where (cs,vs) = unzip ys

          Tuple vs | null vs   -> Just val
                   | otherwise -> Tuple `fmap` delMany vs
          List vs  | null vs -> Just val
                   | otherwise -> List `fmap` delMany vs
          Neg v       -> Neg `fmap` delMaybe v
          Ratio v1 v2 -> do ~[a,b] <- delMany [v1,v2]
                            Just (Ratio a b)
          Integer {}  -> Just val
          Float {}    -> Just val
          Char {}     -> Just val
          String {}   -> Just val