module UU.Scanner.Position where

type Line     = Int
type Column   = Int
type Filename = String


class Position p where 
  line   :: p -> Line
  column :: p -> Column
  file   :: p -> Filename


instance Position Pos where
   line   (Pos l _ _) = l
   column (Pos _ c _) = c
   file   (Pos _ _ f) = f

data Pos = Pos !Line !Column Filename 

instance Show Pos where
  show (Pos l c f) | l == (-1) = ""
                   | otherwise = let file = if null f then "" else show f
                                     lc = "(line " ++ show l ++ ", column " ++ show c ++")"
                                 in file ++ lc
initPos :: FilePath -> Pos
initPos fn = Pos 1 1 fn

noPos :: Pos
noPos = Pos (-1) (-1) ""

advl ::  Line -> Pos ->Pos
advl i (Pos l c f) = (Pos (l+i) 1 f)

advc :: Column -> Pos ->  Pos
advc i (Pos l c f) = (Pos l (c+i) f)

adv :: Pos -> Char -> Pos
adv pos c = case c of
  '\t' -> advc (tabWidth (column pos)) pos
  '\n' -> advl 1 pos
  _    -> advc 1 pos

updPos :: Char -> Pos -> Pos
updPos x = case x of
 '\n' -> newl
 '\t' -> tab
 _    -> advc 1

tab              :: Pos -> Pos
tab  (Pos l c f) =  Pos l (c+tabWidth c) f

newl :: Pos ->Pos
newl =  advl 1

tabWidth :: Column -> Int
tabWidth c = 8 - ((c-1) `mod` 8)


updPos' :: Char -> Pos -> (Pos -> a) -> a
updPos' c p cont = p `seq` cont (updPos c p)

advc' :: Int -> Pos -> (Pos -> a) -> a
advc' i p cont = p `seq` cont (advc i p)

tab' :: Pos -> (Pos -> a) -> a
tab'  p cont = p `seq` cont (tab p)

newl' :: Pos -> (Pos -> a) -> a
newl' p cont = p `seq` cont (newl p)