{-|
Module      : Toml.Position
Description : File position representation
Copyright   : (c) Eric Mertens, 2023
License     : ISC
Maintainer  : emertens@gmail.com

This module provides the 'Position' type for tracking locations
in files while doing lexing and parsing for providing more useful
error messages.

This module assumes 8 column wide tab stops.

-}
module Toml.Position (
    Position(..),
    startPos,
    move,
    ) where

-- | A position in a text file
data Position = Position {
    Position -> Int
posIndex  :: {-# UNPACK #-} !Int, -- ^ code-point index (zero-based)
    Position -> Int
posLine   :: {-# UNPACK #-} !Int, -- ^ line index (one-based)
    Position -> Int
posColumn :: {-# UNPACK #-} !Int  -- ^ column index (one-based)
    } deriving (
        Read    {- ^ Default instance -},
        Show    {- ^ Default instance -},
        Ord     {- ^ Default instance -},
        Eq      {- ^ Default instance -})

-- | The initial 'Position' for the start of a file
startPos :: Position
startPos :: Position
startPos = Position :: Int -> Int -> Int -> Position
Position { posIndex :: Int
posIndex = Int
0, posLine :: Int
posLine = Int
1, posColumn :: Int
posColumn = Int
1 }

-- | Adjust a file position given a single character handling
-- newlines and tabs. All other characters are considered to fill
-- exactly one column.
move :: Char -> Position -> Position
move :: Char -> Position -> Position
move Char
x Position{ posIndex :: Position -> Int
posIndex = Int
i, posLine :: Position -> Int
posLine = Int
l, posColumn :: Position -> Int
posColumn = Int
c} =
    case Char
x of
        Char
'\n' -> Position :: Int -> Int -> Int -> Position
Position{ posIndex :: Int
posIndex = Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1, posLine :: Int
posLine = Int
lInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1, posColumn :: Int
posColumn = Int
1 }
        Char
'\t' -> Position :: Int -> Int -> Int -> Position
Position{ posIndex :: Int
posIndex = Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1, posLine :: Int
posLine = Int
l, posColumn :: Int
posColumn = (Int
c Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
7) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`quot` Int
8 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
8 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 }
        Char
_    -> Position :: Int -> Int -> Int -> Position
Position{ posIndex :: Int
posIndex = Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1, posLine :: Int
posLine = Int
l, posColumn :: Int
posColumn = Int
cInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1 }