--  Compiler Toolkit: miscellaneous utility routines
--
--  Author : Manuel M. T. Chakravarty
--  Created: 8 February 95
--
--  Version $Revision: 1.12 $ from $Date: 2000/02/28 06:28:59 $
--
--  Copyright (c) [1995..2000], Manuel M. T. Chakravarty
--
--  This library is free software; you can redistribute it and/or
--  modify it under the terms of the GNU Library General Public
--  License as published by the Free Software Foundation; either
--  version 2 of the License, or (at your option) any later version.
--
--  This library is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
--  Library General Public License for more details.
--
--- DESCRIPTION ---------------------------------------------------------------
--
--  This module provides miscellaneous utility routines used in different parts
--  of the Compiler Tookit.
--
--- DOCU ----------------------------------------------------------------------
--
--  language: Haskell 98
--
--- TODO ----------------------------------------------------------------------
--

module Text.CTK.Utils (sort, sortBy, lookupBy, indentMultilineString, quantifySubject,
          ordinal, Tag(..), mapMaybeM, mapMaybeM_, mapEitherM, mapEitherM_)
where

import           Data.List (find)


-- list operations
-- ---------------

-- naive sort for a list of which the elements are in the Ord class (EXPORTED)
--
sort      :: Ord a => [a] -> [a]
sort []    = []
sort (m:l) = (sort . filter (< m)) l ++ [m] ++ (sort . filter (>= m)) l


-- naive sort for a list with explicit ordering relation (smaller than)
-- (EXPORTED)
--
sortBy               :: (a -> a -> Bool) -> [a] -> [a]
sortBy _       []     = []
sortBy smaller (m:l)  =    (sortBy smaller . filter (`smaller` m)) l
            ++ [m]
            ++ (sortBy smaller . filter (not . (`smaller` m))) l

-- generic lookup
--
lookupBy      :: (a -> a -> Bool) -> a -> [(a, b)] -> Maybe b
lookupBy eq x  = fmap snd . find (eq x . fst)


-- string operations
-- -----------------

-- string manipulation
--

-- indent the given multiline text by the given number of spaces
--
indentMultilineString   :: Int -> String -> String
indentMultilineString n  = unlines . (map (spaces++)) . lines
               where
                 spaces = take n (repeat ' ')

-- aux. routines for output
--

-- given a number and a string containing the quantified subject, yields two
-- strings; one contains the quantified subject and the other contains ``is''
-- or ``are'' depending on the quantification (EXPORTED)
--
quantifySubject         :: Int -> String -> (String, String)
quantifySubject no subj  = (noToStr no ++ " " ++ subj
                ++ (if plural then "s" else ""),
                if plural then "are" else "is")
               where
                 plural = (no /= 1)

                 noToStr  0 = "no"
                 noToStr  1 = "one"
                 noToStr  2 = "two"
                 noToStr  3 = "three"
                 noToStr  4 = "four"
                 noToStr  5 = "five"
                 noToStr  6 = "six"
                 noToStr  7 = "seven"
                 noToStr  8 = "eight"
                 noToStr  9 = "nine"
                 noToStr 10 = "ten"
                 noToStr 11 = "eleven"
                 noToStr 12 = "twelve"
                 noToStr no = show no

-- stringfy a ordinal number (must be positive) (EXPORTED)
--
ordinal   :: Int -> String
ordinal n  = if n < 0
         then
           error "FATAL ERROR: Utilis: ordinal: Negative number!"
         else
           case (n `mod` 10) of
             1 | n /= 11 -> show n ++ "st"
             2 | n /= 12 -> show n ++ "nd"
             3 | n /= 13 -> show n ++ "rd"
             _           -> show n ++ "th"


-- tags
-- ----

-- tag values of a type are meant to define a mapping that collapses values
-- onto a single integer that are meant to be identified in comparisons etc
--
class Tag a where
  tag :: a -> Int


-- monad operations
-- ----------------

-- maps some monad operation into a `Maybe', yielding a monad
-- providing the mapped `Maybe' as its result (EXPORTED)
--
mapMaybeM            :: Monad m
             => (a -> m b) -> Maybe a -> m (Maybe b)
mapMaybeM m Nothing   = return Nothing
mapMaybeM m (Just a)  = m a >>= \r -> return (Just r)

-- like above, but ignoring the result (EXPORTED)
--
mapMaybeM_     :: Monad m => (a -> m b) -> Maybe a -> m ()
mapMaybeM_ m x  = mapMaybeM m x >> return ()

-- maps monad operations into a `Either', yielding a monad
-- providing the mapped `Either' as its result (EXPORTED)
--
mapEitherM               :: Monad m
                 => (a -> m c)
             -> (b -> m d)
             -> Either a b
             -> m (Either c d)
mapEitherM m n (Left x)   = m x >>= \r -> return (Left r)
mapEitherM m n (Right y)  = n y >>= \r -> return (Right r)

-- like above, but ignoring the result (EXPORTED)
--
mapEitherM_ :: Monad m => (a -> m c) -> (b -> m d) -> Either a b -> m ()
mapEitherM_ m n x = mapEitherM m n x >> return ()
