rakhana-0.1.0.0: Data/Rakhana/XRef.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
--------------------------------------------------------------------------------
-- |
-- Module : Data.Rakhana.XRef
-- Copyright : (C) 2014 Yorick Laupa
-- License : (see the file LICENSE)
--
-- Maintainer : Yorick Laupa <yo.eight@gmail.com>
-- Stability : provisional
-- Portability : non-portable
--
--------------------------------------------------------------------------------
module Data.Rakhana.XRef
( getXRef
, getXRefPos
) where
--------------------------------------------------------------------------------
import qualified Data.ByteString.Char8 as B8
import Data.Char (isDigit, isSpace)
import Data.Typeable
--------------------------------------------------------------------------------
import Control.Monad.Catch (Exception, MonadThrow(..))
import Pipes.Safe ()
--------------------------------------------------------------------------------
import Data.Rakhana.Internal.Parsers
import Data.Rakhana.Internal.Types
import Data.Rakhana.Tape
import Data.Rakhana.Util.Drive
--------------------------------------------------------------------------------
data XRefParsingException
= XRefParsingException String
deriving (Show, Typeable)
--------------------------------------------------------------------------------
instance Exception XRefParsingException
--------------------------------------------------------------------------------
bufferSize :: Int
bufferSize = 4096
--------------------------------------------------------------------------------
getXRefPos :: MonadThrow m => Drive m Integer
getXRefPos
= do driveBottom
driveBackward
skipEOL
parseEOF
skipEOL
p <- parseXRefPosInteger
skipEOL
parseStartXRef
return p
--------------------------------------------------------------------------------
getXRef :: MonadThrow m => Integer -> Drive m XRef
getXRef pos
= do driveTop
driveForward
driveSeek pos
eR <- parseRepeatedly bufferSize parseXRef
either (throwM . XRefParsingException) return eR
--------------------------------------------------------------------------------
skipEOL :: Monad m => Drive m ()
skipEOL
= do bs <- drivePeek 1
case B8.uncons bs of
Just (c, _)
| isSpace c -> driveDiscard 1 >> skipEOL
| otherwise -> return ()
_ -> return ()
--------------------------------------------------------------------------------
parseEOF :: MonadThrow m => Drive m ()
parseEOF
= do bs <- driveGet 5
case bs of
"%%EOF" -> return ()
_ -> throwM $ XRefParsingException "Expected %%EOF"
--------------------------------------------------------------------------------
parseXRefPosInteger :: MonadThrow m => Drive m Integer
parseXRefPosInteger = go []
where
go cs = do bs <- drivePeek 1
case B8.uncons bs of
Just (c,_)
| isDigit c -> driveDiscard 1 >> go (c:cs)
| otherwise -> return $ read cs
_ -> return $ read cs
--------------------------------------------------------------------------------
parseStartXRef :: MonadThrow m => Drive m ()
parseStartXRef
= do bs <- driveGet 9
case bs of
"startxref" -> return ()
_ -> throwM $ XRefParsingException "Expected startxref"