{-|
Module      : Hectoparsec.Pos
Copyright   : (c) comp 2020
License     : MIT
Maintainer  : onecomputer00@gmail.com
Stability   : stable
Portability : portable

Data type for source position.
-}
module Hectoparsec.Pos
    ( -- * Positional data
      Pos(..)
    , initialPos
    , updatePos
    ) where

-- | The position of a token within the source text.
data Pos = Pos
    { Pos -> FilePath
posFile   :: !FilePath           -- ^ The source file path.
    , Pos -> Int
posLine   :: {-# UNPACK #-} !Int -- ^ The line number, starting at 1.
    , Pos -> Int
posColumn :: {-# UNPACK #-} !Int -- ^ The column number, starting at 1.
    }
    deriving (Int -> Pos -> ShowS
[Pos] -> ShowS
Pos -> FilePath
(Int -> Pos -> ShowS)
-> (Pos -> FilePath) -> ([Pos] -> ShowS) -> Show Pos
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [Pos] -> ShowS
$cshowList :: [Pos] -> ShowS
show :: Pos -> FilePath
$cshow :: Pos -> FilePath
showsPrec :: Int -> Pos -> ShowS
$cshowsPrec :: Int -> Pos -> ShowS
Show, Pos -> Pos -> Bool
(Pos -> Pos -> Bool) -> (Pos -> Pos -> Bool) -> Eq Pos
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Pos -> Pos -> Bool
$c/= :: Pos -> Pos -> Bool
== :: Pos -> Pos -> Bool
$c== :: Pos -> Pos -> Bool
Eq)

instance Ord Pos where
    compare :: Pos -> Pos -> Ordering
compare (Pos FilePath
_ Int
l1 Int
c1) (Pos FilePath
_ Int
l2 Int
c2) = Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Int
l1 Int
l2 Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
<> Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Int
c1 Int
c2

-- | Creates a 'Pos' for the start of a source text (line 1, column 1) with a given file path.
initialPos :: FilePath -> Pos
initialPos :: FilePath -> Pos
initialPos FilePath
fp = FilePath -> Int -> Int -> Pos
Pos FilePath
fp Int
1 Int
1
{-# INLINE initialPos #-}

{-|
Updates position based on a character-like token.

Tokens which are considered newlines will increment the line number by 1 and set the column number to 1. Otherwise,
the column number is incremented by one.
-}
updatePos
    :: (a -> Bool) -- ^ Whether the token is a new line.
    -> a           -- ^ The token.
    -> Pos         -- ^ The position to update.
    -> Pos
updatePos :: forall a. (a -> Bool) -> a -> Pos -> Pos
updatePos a -> Bool
isNewline a
tok (Pos FilePath
fp Int
line Int
col)
    | a -> Bool
isNewline a
tok = FilePath -> Int -> Int -> Pos
Pos FilePath
fp (Int
line Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int
1
    | Bool
otherwise     = FilePath -> Int -> Int -> Pos
Pos FilePath
fp Int
line (Int
col Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
{-# INLINE updatePos #-}