content: {-# LANGUAGE DeriveAnyClass #-}
  {-# LANGUAGE DeriveDataTypeable #-}
  {-# LANGUAGE DeriveGeneric #-}
  {-# LANGUAGE Safe #-}
  
  -- |
  -- Module      :  Text.Megaparsec.Pos
  -- Copyright   :  © 2015–present Megaparsec contributors
  -- License     :  FreeBSD
  --
  -- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
  -- Stability   :  experimental
  -- Portability :  portable
  --
  -- Textual source position. The position includes name of file, line number,
  -- and column number.
  --
  -- You probably do not want to import this module directly because
  -- "Text.Megaparsec" re-exports it anyway.
  module Text.Megaparsec.Pos
    ( -- * Abstract position
      Pos,
      mkPos,
      unPos,
      pos1,
      defaultTabWidth,
      InvalidPosException (..),
  
      -- * Source position
      SourcePos (..),
      initialPos,
      sourcePosPretty,
    )
  where
  
  import Control.DeepSeq
  import Control.Exception
  import Data.Data (Data)
  import GHC.Generics
  
  ----------------------------------------------------------------------------
  -- Abstract position
  
  -- | 'Pos' is the type for positive integers. This is used to represent line
  -- number, column number, and similar things like indentation level.
  -- 'Semigroup' instance can be used to safely and efficiently add 'Pos'es
  -- together.
  --
  -- @since 5.0.0
  newtype Pos = Pos Int
    deriving (Show, Eq, Ord, Data, Generic, NFData)
  
  -- | Construction of 'Pos' from 'Int'. The function throws
  -- 'InvalidPosException' when given a non-positive argument.
  --
  -- @since 6.0.0
  mkPos :: Int -> Pos
  mkPos a =
    if a <= 0
      then throw (InvalidPosException a)
      else Pos a
  {-# INLINE mkPos #-}
  
  -- | Extract 'Int' from 'Pos'.
  --
  -- @since 6.0.0
  unPos :: Pos -> Int
  unPos (Pos w) = w
  {-# INLINE unPos #-}
  
  -- | Position with value 1.
  --
  -- @since 6.0.0
  pos1 :: Pos
  pos1 = mkPos 1
  
  -- | Value of tab width used by default. Always prefer this constant when
  -- you want to refer to the default tab width because actual value /may/
  -- change in future.
  --
  -- Currently:
  --
  -- > defaultTabWidth = mkPos 8
  --
  -- @since 5.0.0
  defaultTabWidth :: Pos
  defaultTabWidth = mkPos 8
  
  instance Semigroup Pos where
    (Pos x) <> (Pos y) = Pos (x + y)
    {-# INLINE (<>) #-}
  
  instance Read Pos where
    readsPrec d =
      readParen (d > 10) $ \r1 -> do
        ("Pos", r2) <- lex r1
        (x, r3) <- readsPrec 11 r2
        return (mkPos x, r3)
  
  -- | The exception is thrown by 'mkPos' when its argument is not a positive
  -- number.
  --
  -- @since 5.0.0
  newtype InvalidPosException
    = -- | Contains the actual value that was passed to 'mkPos'
      InvalidPosException Int
    deriving (Eq, Show, Data, Generic)
  
  instance Exception InvalidPosException
  
  instance NFData InvalidPosException
  
  ----------------------------------------------------------------------------
  -- Source position
  
  -- | The data type 'SourcePos' represents source positions. It contains the
  -- name of the source file, a line number, and a column number. Source line
  -- and column positions change intensively during parsing, so we need to
  -- make them strict to avoid memory leaks.
  data SourcePos = SourcePos
    { -- | Name of source file
      sourceName :: FilePath,
      -- | Line number
      sourceLine :: !Pos,
      -- | Column number
      sourceColumn :: !Pos
    }
    deriving (Show, Read, Eq, Ord, Data, Generic)
  
  instance NFData SourcePos
  
  -- | Construct initial position (line 1, column 1) given name of source
  -- file.
  initialPos :: FilePath -> SourcePos
  initialPos n = SourcePos n pos1 pos1
  
  -- | Pretty-print a 'SourcePos'.
  --
  -- @since 5.0.0
  sourcePosPretty :: SourcePos -> String
  sourcePosPretty (SourcePos n l c)
    | null n = showLC
    | otherwise = n <> ":" <> showLC
    where
      showLC = show (unPos l) <> ":" <> show (unPos c)
  