content: {-# LANGUAGE CPP #-}
  
  module Floskell.Attoparsec ( Position, parseOnly ) where
  
  import qualified Data.Attoparsec.ByteString as AP
  import qualified Data.ByteString            as B
  import           Data.Semigroup             as Sem
  import           Data.Word                  ( Word8 )
  
  data Position = Position { posLine :: !Int, posColumn :: !Int }
      deriving ( Eq, Ord, Show )
  
  instance Sem.Semigroup Position where
      (Position l c) <> (Position l' c') =
          Position (l + l') (if l' > 0 then c' else c + c')
  
  instance Monoid Position where
      mempty = Position 0 0
  
  #if !(MIN_VERSION_base(4,11,0))
      mappend = (<>)
  #endif
  
  advance :: Position -> Word8 -> Position
  advance (Position l _) 10 = Position (l + 1) 0
  advance (Position l c) _ = Position l (c + 1)
  
  position :: B.ByteString -> Position
  position = B.foldl' advance mempty
  
  positionAt :: B.ByteString -> B.ByteString -> Position
  positionAt l r = position $ B.take (B.length l - B.length r) l
  
  parseOnly :: AP.Parser a -> B.ByteString -> Either String a
  parseOnly p b = case AP.feed (AP.parse p b) B.empty of
      AP.Done _ x -> Right x
      AP.Fail r _ err -> Left $ parseError (positionAt b r) err
      AP.Partial _ -> error "impossible"
    where
      parseError (Position l c) err = err ++ ", at line " ++ show (l + 1)
          ++ ", column " ++ show c
  