packages feed

BiobaseEnsembl-0.2.0.1: Biobase/GTF/Import.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}

-- | Parses GTF

module Biobase.GTF.Import (gtfFromFile,
                             parseGTFs,
                            ) where

import Prelude hiding (takeWhile)
import Data.Attoparsec.ByteString.Char8 hiding (isSpace)
import qualified Data.Attoparsec.ByteString.Lazy as L
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Builder as S
import qualified Data.ByteString.Lazy.Char8 as B
import qualified Data.Vector as V
import System.Directory
import Data.Char
import Control.Monad
import Debug.Trace
import Text.Printf
import Biobase.GTF.Types
import qualified Data.Word8 as W

-- | reads and parses GTF from provided filePath
gtfFromFile :: String -> IO [GTF]
gtfFromFile filePath = do
  printf "# reading GTF from file %s\n" filePath
  fileExists <- doesFileExist filePath
  if fileExists
     then parseGTFs <$> B.readFile filePath
     else fail ("# GTF file \"%s\" does not exist\n" ++ filePath)

-- | Read a lazy bytestring and stream out a list of @GTFs@'s.
-- In case, there is a parse error "late" in the file, we might have
-- already streamed out some (or many!) of these results.

parseGTFs :: B.ByteString -> [GTF]
parseGTFs = go
  where go xs = case L.parse genParseGTF xs of
          L.Fail remainingInput ctxts err  -> error $ "parseGTFs failed! " ++ err ++ " ctxt: " ++ show ctxts ++ " head of remaining input: " ++ B.unpack (B.take 1000 remainingInput)
          L.Done remainingInput btr
            | B.null remainingInput  -> [btr]
            | otherwise              -> btr : go remainingInput

genParseGTF :: Parser GTF
genParseGTF = do
  skipMany (try genParseGTFComment)
  _entry <- many1 (try genParseGTFEntry) <?> "GTF entry"
  return $ GTF (V.fromList _entry) B.empty

genParseGTFComment :: Parser String
genParseGTFComment = do
  string "#"
  takeWhile1 (/= '\n')
  endOfLine
  return $ ""

genParseGTFEntry :: Parser GTFEntry
genParseGTFEntry = do
  _gtfSeqid <- takeWhile1 (/= '\t') <?> "seqid"
  char '\t'
  _gtfSource <- takeWhile1 (/= '\t') <?> "source"
  char '\t'
  _gtfType <- takeWhile1 (/= '\t') <?> "type"
  char '\t'
  _gtfStart <- decimal  <?> "start"
  char '\t'
  _gtfEnd <- decimal <?> "end"
  char '\t'
  _gtfScore <- takeWhile1 (/= '\t') <?> "score"
  char '\t'
  _gtfStrand <- (choice [char '+', char '-', char '.']) <?> "strand"
  char '\t'
  _gtfPhase <- takeWhile1 (/= '\t') <?> "phase"
  char '\t'
  _gtfAttributes <- genParseGTFAttributes <?> "GTF attributes"
  endOfLine
  skipMany (try genParseGTFComment)
  return $ GTFEntry (B.fromStrict _gtfSeqid) (B.fromStrict _gtfSource) (B.fromStrict _gtfType) _gtfStart _gtfEnd (B.fromStrict _gtfScore) _gtfStrand (B.fromStrict _gtfPhase) (V.fromList _gtfAttributes)

genParseGTFAttributes :: Parser [B.ByteString]
genParseGTFAttributes = do
  _gtfAtributesString <- takeTill (\a -> a == '\n') <?> "attributes"
  let _gtfAtributes = map B.fromStrict (C.split ';' _gtfAtributesString)
  return $ _gtfAtributes

toLB :: C.ByteString -> B.ByteString
toLB = S.toLazyByteString . S.byteString