    {-# LANGUAGE CPP, DeriveDataTypeable #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Language.Haskell.Exts.SrcLoc
    -- Copyright   :  (c) Niklas Broberg 2009
    -- License     :  BSD-style (see the file LICENSE.txt)
    --
    -- Maintainer  :  Niklas Broberg, d00nibro@chalmers.se
    -- Stability   :  stable
    -- Portability :  portable
    --
    -- This module defines various data types representing source location
    -- information, of varying degree of preciseness.
    --
    -----------------------------------------------------------------------------
    module Language.Haskell.Exts.SrcLoc where

    #ifdef __GLASGOW_HASKELL__
    #ifdef BASE4
    import Data.Data
    #else
    import Data.Generics (Data(..),Typeable(..))
    #endif
    #endif

    -- | A single position in the source.
    data SrcLoc = SrcLoc
        { srcFilename :: String
        , srcLine :: Int
        , srcColumn :: Int
        }
    #ifdef __GLASGOW_HASKELL__
      deriving (Eq,Ord,Show,Typeable,Data)
    #else
      deriving (Eq,Ord,Show)
    #endif

    -- | A portion of the source, spanning one or more lines and zero or more columns.
    data SrcSpan = SrcSpan
        { srcSpanFilename    :: String
        , srcSpanStartLine   :: Int
        , srcSpanStartColumn :: Int
        , srcSpanEndLine     :: Int
        , srcSpanEndColumn   :: Int
        }
    #ifdef __GLASGOW_HASKELL__
      deriving (Eq,Ord,Show,Typeable,Data)
    #else
      deriving (Eq,Ord,Show)
    #endif

    -- | Returns 'srcSpanStartLine' and 'srcSpanStartColumn' in a pair.
    srcSpanStart :: SrcSpan -> (Int,Int)
    srcSpanStart x = (srcSpanStartLine x, srcSpanStartColumn x)

    -- | Returns 'srcSpanEndLine' and 'srcSpanEndColumn' in a pair.
    srcSpanEnd :: SrcSpan -> (Int,Int)
    srcSpanEnd x = (srcSpanEndLine x, srcSpanEndColumn x)

    -- | Combine two locations in the source to denote a span.
    mkSrcSpan :: SrcLoc -> SrcLoc -> SrcSpan
    mkSrcSpan (SrcLoc fn sl sc) (SrcLoc _ el ec) = SrcSpan fn sl sc el ec

    -- | Merge two source spans into a single span from the start of the first
    --   to the end of the second. Assumes that the two spans relate to the
    --   same source file.
    mergeSrcSpan :: SrcSpan -> SrcSpan -> SrcSpan
    mergeSrcSpan (SrcSpan fn sl1 sc1 el1 ec1) (SrcSpan _ sl2 sc2 el2 ec2) =
        let (sl,sc) = min (sl1,sc1) (sl2,sc2)
            (el,ec) = max (el1,ec1) (el2,ec2)
         in SrcSpan fn sl sc el ec

    -- | Test if a given span starts and ends at the same location.
    isNullSpan :: SrcSpan -> Bool
    isNullSpan ss = srcSpanStartLine ss == srcSpanEndLine ss &&
                        srcSpanStartColumn ss >= srcSpanEndColumn ss

    -- | An entity located in the source.
    data Loc a = Loc
        { loc :: SrcSpan
        , unLoc :: a
        }
      deriving (Eq,Ord,Show)

    -- | A portion of the source, extended with information on the position of entities within the span.
    data SrcSpanInfo = SrcSpanInfo
        { srcInfoSpan    :: SrcSpan
    --    , explLayout     :: Bool
        , srcInfoPoints  :: [SrcSpan]    -- Marks the location of specific entities inside the span
        }
    #ifdef __GLASGOW_HASKELL__
      deriving (Eq,Ord,Show,Typeable,Data)
    #else
      deriving (Eq,Ord,Show)
    #endif

    -- | Generate a 'SrcSpanInfo' with no positional information for entities.
    noInfoSpan :: SrcSpan -> SrcSpanInfo
    noInfoSpan ss = SrcSpanInfo ss []

    -- | Generate a 'SrcSpanInfo' with the supplied positional information for entities.
    infoSpan :: SrcSpan -> [SrcSpan] -> SrcSpanInfo
    infoSpan = SrcSpanInfo

    -- | Combine two 'SrcSpanInfo's into one that spans the combined source area of
    --   the two arguments, leaving positional information blank.
    combSpanInfo :: SrcSpanInfo -> SrcSpanInfo -> SrcSpanInfo
    combSpanInfo s1 s2 = SrcSpanInfo
        (mergeSrcSpan (srcInfoSpan s1) (srcInfoSpan s2))
        []

    -- | Short name for 'combSpanInfo'
    (<++>) :: SrcSpanInfo -> SrcSpanInfo -> SrcSpanInfo
    (<++>) = combSpanInfo

    -- | Optionally combine the first argument with the second,
    --   or return it unchanged if the second argument is 'Nothing'.
    (<+?>) :: SrcSpanInfo -> Maybe SrcSpanInfo -> SrcSpanInfo
    a <+?> b = case b of {Nothing -> a; Just b -> a <++> b}

    -- | Optionally combine the second argument with the first,
    --   or return it unchanged if the first argument is 'Nothing'.
    (<?+>) :: Maybe SrcSpanInfo -> SrcSpanInfo -> SrcSpanInfo
    a <?+> b = case a of {Nothing -> b; Just a -> a <++> b}

    -- | Add more positional information for entities of a span.
    (<**) :: SrcSpanInfo -> [SrcSpan] -> SrcSpanInfo
    ss@(SrcSpanInfo {srcInfoPoints = ps}) <** xs = ss {srcInfoPoints = ps ++ xs}

    -- | Merge two 'SrcSpan's and lift them to a 'SrcInfoSpan' with
    --   no positional information for entities.
    (<^^>) :: SrcSpan -> SrcSpan -> SrcSpanInfo
    a <^^> b = noInfoSpan (mergeSrcSpan a b)

    infixl 6 <^^>
    infixl 5 <++>
    infixl 4 <**, <+?>, <?+>

    -- | A class to work over all kinds of source location information.
    class SrcInfo si where
      toSrcInfo   :: SrcLoc -> [SrcSpan] -> SrcLoc -> si
      fromSrcInfo :: SrcSpanInfo -> si
      getPointLoc :: si -> SrcLoc
      fileName    :: si -> String
      startLine   :: si -> Int
      startColumn :: si -> Int

      getPointLoc si = SrcLoc (fileName si) (startLine si) (startColumn si)

    instance SrcInfo SrcLoc where
      toSrcInfo s _ _ = s
      fromSrcInfo si = SrcLoc (fileName si) (startLine si) (startColumn si)
      fileName = srcFilename
      startLine = srcLine
      startColumn = srcColumn

    instance SrcInfo SrcSpan where
      toSrcInfo st _ end = mkSrcSpan st end
      fromSrcInfo = srcInfoSpan
      fileName = srcSpanFilename
      startLine = srcSpanStartLine
      startColumn = srcSpanStartColumn

    instance SrcInfo SrcSpanInfo where
      toSrcInfo st pts end = SrcSpanInfo (mkSrcSpan st end) pts
      fromSrcInfo = id
      fileName = fileName . srcInfoSpan
      startLine = startLine . srcInfoSpan
      startColumn = startColumn . srcInfoSpan