{-# LANGUAGE RankNTypes, TypeFamilies, GeneralizedNewtypeDeriving #-}

-- Module      :  Text.ParserCombinators.Parsek
-- Copyright   :  Koen Claessen 2003
-- License     :  GPL
--
-- Maintainer  :  JP Bernardy
-- Stability   :  provisional
-- Portability :  portable
--
-- This module provides the /Parsek/ library developed by Koen Claessen in his
-- functional pearl article /Parallel Parsing Processes/, Journal of Functional
-- Programming, 14(6), 741–757, Cambridge University Press, 2004:

module Text.ParserCombinators.Parsek.Position
  ( module Text.ParserCombinators.Parsek
  , module Text.ParserCombinators.Class
  , SourcePos(..)
  , Parser
  , getPosition
  , parse
  , parseFromFile
  , maybePosToPos
  , anyChar
  ) where

import Text.ParserCombinators.Class
import Text.ParserCombinators.Parsek hiding (parse,parseFromFile,Parser)
import qualified Text.ParserCombinators.Parsek as P
import Data.Bits
import Control.Monad.Fail as Fail

newtype Parser a = PP (P.Parser (Char, SourcePos) a)
  deriving (Alternative, Applicative, Monad, Functor, MonadPlus, MonadFail)

instance IsParser Parser where
  type SymbolOf Parser = Char
  satisfy p = PP $ fst <$> satisfy (p . fst)
  look = PP $ (map fst) <$> look
  label lab (PP p) = PP (label lab p)
  (PP p) <<|> (PP q) = PP (p <<|> q)

anyChar   :: IsParser p => p (SymbolOf p)
anyChar   = anySymbol

getPosition :: Parser SourcePos
getPosition = PP $ (\l -> case l of
                  [] -> EOF
                  ((_,p):_) -> p) <$> look

parse :: FilePath -> Parser a -> (forall s. ParseMethod s a r) -> String -> ParseResult SourcePos r
parse file (PP p) method s = mapErrR snd $ P.parse p method (zip s (scanl updLoc (initLoc file) s))

parseFromFile :: Parser a -> (forall s. ParseMethod s a r) -> FilePath -> IO (ParseResult SourcePos r)
parseFromFile p method file = parse file p method <$> readFile file

maybePosToPos = maybe EOF id

-------------
-- Locations

data SourcePos = Loc {sourceName :: !FilePath, sourceLine :: !Int, sourceCol :: !Int} | EOF
  deriving (Ord,Eq)

instance Show SourcePos where
   show EOF = "end of file"
   show (Loc f l c) = f ++ ":" ++ show l ++ ":" ++ show c


updLoc :: SourcePos -> Char -> SourcePos
updLoc (Loc f l _) '\n' = Loc f (l+1) 0
updLoc (Loc f l c) '\t' = Loc f l ((c+8) .&. complement 7)
updLoc (Loc f l c) _    = Loc f l (c+1)
updLoc EOF         _    = EOF

initLoc :: FilePath -> SourcePos
initLoc p = Loc p 1 0