biostockholm (empty) → 0.1
raw patch · 4 files changed
+638/−0 lines, 4 filesdep +HUnitdep +basedep +biocoresetup-changed
Dependencies added: HUnit, base, biocore, bytestring, containers, deepseq, explicit-exception, hspec
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- biostockholm.cabal +56/−0
- src/Bio/Sequence/Stockholm.hs +550/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2011, Felipe Lessa++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Felipe Lessa nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ biostockholm.cabal view
@@ -0,0 +1,56 @@+Name: biostockholm+Version: 0.1+Synopsis: Reading and writing Stockholm files (multiple sequence alignment, used by Rfam and Infernal).+License: BSD3+License-file: LICENSE+Author: Felipe Lessa+Maintainer: felipe.lessa@gmail.com+Category: Bioinformatics+Build-type: Simple+Cabal-version: >=1.8+Description:+ Parsing and pretty printing of files in Stockholm 1.0 format. See:+ .+ * <http://sonnhammer.sbc.su.se/Stockholm.html>+ .+ * <ftp://ftp.sanger.ac.uk/pub/databases/Pfam/current_release/relnotes.txt>+ .+ * <http://en.wikipedia.org/wiki/Stockholm_format>++Source-repository head+ Type: darcs+ Location: http://patch-tag.com/r/felipe/biostockholm++Library+ Hs-Source-Dirs: src+ Exposed-modules:+ Bio.Sequence.Stockholm+ Ghc-Options: -Wall+ Build-depends:+ base >= 3 && < 5+ , containers >= 0.2 && < 0.5+ , bytestring == 0.9.*+ , deepseq == 1.1.*+ , explicit-exception == 0.1.*++ , biocore >= 0.1 && < 0.3++Test-suite runtests+ Type: exitcode-stdio-1.0+ Hs-Source-Dirs: src+ Main-is: runtests.hs+ Other-modules:+ Bio.Sequence.Stockholm+ Ghc-Options: -Wall+ Cpp-Options: -DTEST+ Build-depends:+ base >= 3 && < 5+ , containers >= 0.2 && < 0.5+ , bytestring == 0.9.*+ , deepseq == 1.1.*+ , explicit-exception == 0.1.*++ , biocore >= 0.1 && < 0.3++ , hspec == 0.9.*+ , HUnit == 1.2.*
+ src/Bio/Sequence/Stockholm.hs view
@@ -0,0 +1,550 @@+{-# LANGUAGE CPP, EmptyDataDecls, DeriveDataTypeable, OverloadedStrings #-}++-- | Parsing and pretty printing of files in Stockholm 1.0+-- format. See:+--+-- - <http://sonnhammer.sbc.su.se/Stockholm.html>+--+-- - <ftp://ftp.sanger.ac.uk/pub/databases/Pfam/current_release/relnotes.txt>+--+-- - <http://en.wikipedia.org/wiki/Stockholm_format>++module Bio.Sequence.Stockholm+ (-- * Data types+ Stockholm(..)+ ,StockholmSeq(..)+ ,Ann(..)+ ,FileAnnotation(..)+ ,SequenceAnnotation(..)+ ,ColumnAnnotation(..)+ ,InFile+ ,InSeq+ ,findAnn++ -- * Parsing+ ,parseStockholm+ ,StockholmExc(..)++ -- * Printing+ ,prettyPrintStockholm++#ifdef TEST+ -- * Test cases+ ,test_Stockholm+#endif+ )+ where++-- from base+import Control.Applicative ((<$>))+import Control.Arrow (second)+import Control.DeepSeq (NFData(..))+import Control.Monad (mplus)+import Data.Char (isSpace)+import Data.List (foldl', find)+import Data.Maybe (fromMaybe)+import Data.Typeable (Typeable)+import Text.Show (showParen, showString)++-- from containers+import qualified Data.Map as M++-- from bytestring+import qualified Data.ByteString.Lazy.Char8 as B+import Data.ByteString.Lazy.Char8 (ByteString)++-- from biocore+import Bio.Core.Sequence++-- from explicit-exception+import Control.Monad.Exception.Synchronous (Exceptional, throw)+++#ifdef TEST+import Test.Hspec.Monadic+import Test.Hspec.HUnit ()+import Test.HUnit+#endif+++-- | An Stockholm 1.0 formatted file represented in memory.+data Stockholm = Stockholm [Ann FileAnnotation]+ [Ann (ColumnAnnotation InFile)]+ [StockholmSeq]+ deriving (Show, Eq, Typeable)++instance NFData Stockholm where+ rnf (Stockholm file clmn seqs) = rnf file `seq` rnf clmn `seq` rnf seqs++-- | A sequence in Stockholm 1.0 format.+data StockholmSeq = StSeq !SeqLabel+ !SeqData+ [Ann SequenceAnnotation]+ [Ann (ColumnAnnotation InSeq)]+ deriving (Eq, Typeable)++-- We don't derive Show to be able support biocore-0.1, which+-- doesn't have Show instances for SeqLabel and SeqData.+instance Show StockholmSeq where+ showsPrec prec (StSeq (SeqLabel l) (SeqData d) sa ca) =+ showParen (prec > 10) $+ showString "StSeq (SeqLabel " .+ showsPrec 11 l .+ showString ") (SeqData " .+ showsPrec 11 d .+ (')':) . (' ':) .+ showsPrec 11 sa .+ (' ':) .+ showsPrec 11 ca+++instance NFData StockholmSeq where+ rnf (StSeq _ _ sa ca) = rnf sa `seq` rnf ca++instance BioSeq StockholmSeq where+ seqlabel (StSeq sl _ _ _) = sl+ seqdata (StSeq _ sd _ _) = sd+ seqlength (StSeq _ sd _ _) = Offset $ B.length (unSD sd)++-- | A generic annotation.+data Ann d = Ann { feature :: !d+ , text :: !ByteString+ }+ deriving (Show, Eq, Ord, Typeable)++instance NFData (Ann d) where+ -- already strict, default instance++-- | Possible file annotations.+data FileAnnotation =+ AC -- ^ Accession number: Accession number in form PFxxxxx.version or PBxxxxxx.+ | ID -- ^ Identification: One word name for family.+ | DE -- ^ Definition: Short description of family.+ | AU -- ^ Author: Authors of the entry.+ | SE -- ^ Source of seed: The source suggesting the seed members belong to one family.+ | GA -- ^ Gathering method: Search threshold to build the full alignment.+ | TC -- ^ Trusted Cutoff: Lowest sequence score and domain score of match in the full alignment.+ | NC -- ^ Noise Cutoff: Highest sequence score and domain score of match not in full alignment.+ | TP -- ^ Type: Type of family (presently Family, Domain, Motif or Repeat).+ | SQ -- ^ Sequence: Number of sequences in alignment.+ | AM -- ^ Alignment Method: The order ls and fs hits are aligned to the model to build the full align.++ | DC -- ^ Database Comment: Comment about database reference.+ | DR -- ^ Database Reference: Reference to external database.+ | RC -- ^ Reference Comment: Comment about literature reference.+ | RN -- ^ Reference Number: Reference Number.+ | RM -- ^ Reference Medline: Eight digit medline UI number.+ | RT -- ^ Reference Title: Reference Title.+ | RA -- ^ Reference Author: Reference Author+ | RL -- ^ Reference Location: Journal location.+ | PI -- ^ Previous identifier: Record of all previous ID lines.+ | KW -- ^ Keywords: Keywords.+ | CC -- ^ Comment: Comments.+ | NE -- ^ Pfam accession: Indicates a nested domain.+ | NL -- ^ Location: Location of nested domains - sequence ID, start and end of insert.++ | F_Other !ByteString -- ^ Other file annotation.+ deriving (Show, Eq, Ord, Typeable)++-- | Possible column annotations. Phantom type can be 'InFile'+-- or 'InSeq'.+data ColumnAnnotation a =+ SS -- ^ Secondary structure.+ | SA -- ^ Surface accessibility.+ | TM -- ^ TransMembrane.+ | PP -- ^ Posterior probability.+ | LI -- ^ LIgand binding.+ | AS -- ^ Active site.+ | PAS -- ^ AS - Pfam predicted.+ | SAS -- ^ AS - from SwissProt.+ | IN -- ^ INtron (in or after).++ | C_Other !ByteString -- ^ Other column annotation.+ deriving (Show, Eq, Ord, Typeable)++-- | Phantom type for 'ColumnAnnotation's of the whole file.+data InFile+-- | Phantom type for 'ColumnAnnotation's of a single sequence.+data InSeq++-- | Possible sequence annotations.+data SequenceAnnotation =+ S_AC -- ^ Accession number+ | S_DE -- ^ Description+ | S_DR -- ^ Database reference+ | OS -- ^ Organism (species)+ | OC -- ^ Organism classification (clade, etc.)+ | LO -- ^ Look (Color, etc.)++ | S_Other !ByteString -- ^ Other sequence annotation.+ deriving (Show, Eq, Ord, Typeable)+++-- | Class used internally below just to simplify the code.+class IsAnnotation a where+ parseAnn :: ByteString -> a+ showAnn :: a -> ByteString++mkParseAnn :: (ByteString -> a -> ByteString) -> [(ByteString, a)]+ -> (ByteString -> a) -> ByteString -> a+mkParseAnn modify anns mkOther =+ let annots = M.fromList anns+ in \feat -> let featMod = modify feat (error "mkParseAnn: never here")+ in fromMaybe (mkOther feat) $ M.lookup featMod annots++mkShowAnn :: Ord a => (ByteString -> a -> ByteString) -> [(ByteString, a)]+ -> (a -> Maybe ByteString) -> a -> ByteString+mkShowAnn modify anns fromOther =+ let annots = M.fromList [(a,b) | (b,a) <- anns]+ in \ann -> fromMaybe (error "mkShowAnn: never here 2") $+ fromOther ann `mplus` (mod' <$> M.lookup ann annots)+ where mod' = flip modify (error "mkShowAnn: never here 1")++instance IsAnnotation SequenceAnnotation where+ parseAnn = mkParseAnn const seqAnns S_Other+ showAnn = mkShowAnn const seqAnns f+ where f (S_Other o) = Just o+ f _ = Nothing++seqAnns :: [(ByteString, SequenceAnnotation)]+seqAnns = [("LO",LO), ("OC",OC), ("OS",OS),+ ("AC",S_AC), ("DE",S_DE), ("DR",S_DR)]+++instance IsAnnotation FileAnnotation where+ parseAnn = mkParseAnn const fileAnns F_Other+ showAnn = mkShowAnn const fileAnns f+ where f (F_Other o) = Just o+ f _ = Nothing++fileAnns :: [(ByteString, FileAnnotation)]+fileAnns = [("AC",AC), ("AM",AM), ("AU",AU), ("CC",CC),+ ("DC",DC), ("DE",DE), ("DR",DR), ("GA",GA),+ ("ID",ID), ("KW",KW), ("NC",NC), ("NE",NE),+ ("NL",NL), ("PI",PI), ("RA",RA), ("RC",RC),+ ("RL",RL), ("RM",RM), ("RN",RN), ("RT",RT),+ ("SE",SE), ("SQ",SQ), ("TC",TC), ("TP",TP)]+++instance ClmnAnnLoc a => IsAnnotation (ColumnAnnotation a) where+ parseAnn = mkParseAnn removeSuffix clmnAnns C_Other+ where+ removeSuffix feat phantom =+ let suffix = clmnAnnSuffix phantom+ (f, s) = B.splitAt (B.length feat - B.length suffix) feat+ in if suffix == s then f else ""+ showAnn = mkShowAnn addSuffix clmnAnns f+ where+ f (C_Other o) = Just o+ f _ = Nothing+ addSuffix feat phantom = feat `B.append` clmnAnnSuffix phantom++clmnAnns :: [(ByteString, ColumnAnnotation a)]+clmnAnns = [("AS",AS), ("IN",IN), ("LI",LI), ("PAS",PAS), ("PP",PP),+ ("SA",SA), ("SAS",SAS), ("SS",SS), ("TM",TM)]++class ClmnAnnLoc a where+ clmnAnnSuffix :: b a -> ByteString+instance ClmnAnnLoc InSeq where+ clmnAnnSuffix _ = ""+instance ClmnAnnLoc InFile where+ clmnAnnSuffix _ = "_cons"++parseAnn' :: IsAnnotation a => ByteString -> ByteString -> Ann a+parseAnn' = Ann . parseAnn+++++#ifdef TEST+test_parseAnnots :: Specs+test_parseAnnots =+ describe "parse*" $ do+ it "1" $ parseAnn "AC" @?= AC+ it "2" $ parseAnn "SQ" @?= SQ+ it "3a" $ parseAnn "SS" @?= (SS :: ColumnAnnotation InSeq)+ it "3b" $ parseAnn "SS" @?= (C_Other "SS" :: ColumnAnnotation InFile)+ it "4a" $ parseAnn "SS_cons" @?= (SS :: ColumnAnnotation InFile)+ it "4b" $ parseAnn "SS_cons" @?= (C_Other "SS_cons" :: ColumnAnnotation InSeq)+ it "5a" $ parseAnn "SS_CONS" @?= (C_Other "SS_CONS" :: ColumnAnnotation InSeq)+ it "5b" $ parseAnn "SS_CONS" @?= (C_Other "SS_CONS" :: ColumnAnnotation InFile)+ it "6" $ parseAnn "LO" @?= LO+#endif+++-- | Find an annotation. For example, you may use @'findAnn' 'SS'@+-- to find the secondary of an Stockholm file.+findAnn :: Eq d => d -> [Ann d] -> Maybe ByteString+findAnn x = fmap text . find ((== x) . feature)+++-- | Exceptions that may happen while parsing a Stockholm file.+class StockholmExc e where+ -- | File is empty.+ emptyFileExc :: e++ -- | Header is missing.+ headerExc :: e++ -- | Malformed annotation. The line is passed as argument.+ malformedAnnExc :: ByteString -> e++ -- | Unknown annotation type.+ unknownAnnTypeExc :: Char -> e++ -- | Malformed sequence line. The line is passed as argument.+ malformedSeqDataExc :: ByteString -> e++instance StockholmExc () where+ emptyFileExc = ()+ headerExc = ()+ malformedAnnExc _ = ()+ unknownAnnTypeExc _ = ()+ malformedSeqDataExc _ = ()++instance StockholmExc ByteString where+ emptyFileExc = "parseStockholm: empty file."+ headerExc = "parseStockholm: header is missing."+ malformedAnnExc line =+ B.concat ["parseStockholm: malformed annotation '", line, "'."]+ unknownAnnTypeExc typ =+ B.concat ["parseStockholm: unknown annotation type '", B.pack [typ], "'."]+ malformedSeqDataExc line =+ B.concat ["parseStockholm: malformed sequence data line '", line, "'."]+++++++++++++++++-- | Type (F, C, S or R), corresponding sequence, name and data.+type ParseAnnRet = ((Char, Maybe SeqLabel, ByteString), ByteString)++-- | @parseAnnotation line@ tries to parse a line as an annotation.+parseAnnotation :: (StockholmExc e) => ByteString -> Exceptional e ParseAnnRet+parseAnnotation line+ | not (B.isPrefixOf "#=G" line) || B.length line < 5 =+ throw (malformedAnnExc line)+parseAnnotation line =+ let Just (typ, rest) = B.uncons $ B.drop 3 line+ (word1, text1) = second dropSpace . B.break isSpace $ dropSpace rest+ (word2, text2) = second dropSpace . B.break isSpace $ text1+ global = ((typ, Nothing, word1), text1)+ seqspe = ((typ, Just (SeqLabel word1), word2), text2)+ in case typ of+ 'F' -> return global+ 'C' -> return global+ 'S' -> return seqspe+ 'R' -> return seqspe+ _ -> throw (unknownAnnTypeExc typ)++dropSpace :: ByteString -> ByteString+dropSpace = B.dropWhile isSpace+++-- | @parseSeqData line@ tries to parse a line as some data of a sequence.+parseSeqData :: (StockholmExc e) => ByteString+ -> Exceptional e (SeqLabel, SeqData)+parseSeqData str = case B.words str of+ [ident, sq] -> return (SeqLabel ident, SeqData sq)+ _ -> throw (malformedSeqDataExc str)+++-- | @parseStockholm@ parses a file in Stockholm 1.0 format.+--+-- Each file must be completely read before it is used because+-- the Stockholm format allows information to be given in any+-- part of the file. However, there may be multiple+-- \"Stockholm files\" concatenated in a single \"filesystem+-- file\". These multiple files are read independently, which+-- is why we return a list of 'Exceptional'@s@.+--+-- If you prefer to read the whole file in one go, use+-- @'sequence' (parseStockholm input)@, which will fail if any+-- family fails.+parseStockholm :: (StockholmExc e) => ByteString+ -> [Exceptional e Stockholm]+parseStockholm = map parseStockholm' . split .+ filter (not . B.all isSpace) . B.lines+ where+ split [] = []+ split xs = case break (B.isPrefixOf "//") xs of+ (y, ys) -> y : split (tail ys)++parseStockholm' :: (StockholmExc e) => [ByteString]+ -> Exceptional e Stockholm+parseStockholm' = header . filter (not . B.null)+ where+ -- Find+ header (h:hs)+ | h == stockholm = do (annots,seqs) <- go initial hs+ return (makeStockholm annots seqs)+ | otherwise = throw headerExc+ where stockholm = "# STOCKHOLM 1.0"+ initial = (M.empty, M.empty)+ header [] = throw emptyFileExc++ -- End of file+ go acc [] = return acc++ -- Annotation+ go (annots,seqs) (line:ls) | B.take 2 line == "#=" = do+ annot <- parseAnnotation line+ go (insertDM annot annots, seqs) ls++ -- Comment+ go (annots,seqs) (l:ls) | B.head l == '#' =+ go (annots,seqs) ls++ -- Otherwise a sequence+ go (annots,seqs) (line:ls) = do+ seqData <- parseSeqData line+ go (annots, insertDM seqData seqs) ls+++type DiffMap a b = M.Map a ([b] -> [b])++insertDM :: Ord a => (a,b) -> DiffMap a b -> DiffMap a b+insertDM (key,val) = M.insertWith (flip (.)) key (val:)++finishDM :: (b -> ByteString) -> DiffMap a b -> M.Map a ByteString+finishDM f = fmap (B.concat . map f . ($ []))++-- | Glue everything in place, as the Stockholm format lets+-- everything be everywhere and split in any number of parts.+makeStockholm :: DiffMap (Char, Maybe SeqLabel, ByteString) ByteString+ -> DiffMap SeqLabel SeqData -> Stockholm+makeStockholm annotsDM seqsDM =+ let annots = finishDM id annotsDM+ seqs = finishDM unSD seqsDM++ -- Separate the global from the sequence annotations+ go ('F', Nothing, feat) txt (f,c,r) = (parseAnn' feat txt:f,c,r)+ go ('C', Nothing, feat) txt (f,c,r) = (f,parseAnn' feat txt:c,r)+ go (typ, Just sq, feat) txt (f,c,r) = (f,c,(typ,sq,feat,txt):r)+ go _ _ _ = error "makeStockholm: not here, ever"++ -- Separate the sequence annotations to each annotation+ add ('S',sq,feat,txt) = flip M.adjust sq $ \(StSeq sl sd sa ca) ->+ StSeq sl sd (parseAnn' feat txt : sa) ca+ add ('R',sq,feat,txt) = flip M.adjust sq $ \(StSeq sl sd sa ca) ->+ StSeq sl sd sa (parseAnn' feat txt : ca)+ add _ = error "makeStockholm: not here either"++ -- Glue everything+ (file, clmn, rest) = M.foldWithKey go ([],[],[]) annots+ plainseqs = M.mapWithKey (\k s -> StSeq k (SeqData s) [] []) seqs+ in Stockholm file clmn (M.elems $ foldl' (flip add) plainseqs rest)+++++-- | Pretty-prints an Stockholm file. We follow Rfam preferences+-- and do not wrap lines.+prettyPrintStockholm :: Stockholm -> B.ByteString+prettyPrintStockholm (Stockholm file clmn seqs) =+ let showAnnF :: IsAnnotation a => Char -> Ann a -> (ByteString, ByteString)+ showAnnF t ann = (B.concat [B.pack ("#=G" ++ t : " "),+ showAnn (feature ann)], text ann)+ showAnnS :: IsAnnotation a => ByteString -> Char -> Ann a -> (ByteString, ByteString)+ showAnnS s t ann = (B.unwords [B.pack ("#=G" ++ [t]), s,+ showAnn (feature ann)], text ann)++ fileLines = map (showAnnF 'F') file+ clmnLines = map (showAnnF 'C') clmn+ sequences = do+ StSeq (SeqLabel name) (SeqData seqd) sa ca <- seqs+ (name, seqd) : map (showAnnS name 'R') ca+ ++ map (showAnnS name 'S') sa++ allLines = fileLines ++ sequences ++ clmnLines+ firstColLen = maximum $ map (B.length . fst) allLines+ mkLine (col1, col2) = B.concat [col1, B.replicate n ' ', col2]+ where n = 1 + firstColLen - B.length col1+ in B.unlines ("# STOCKHOLM 1.0" : map mkLine allLines ++ ["//"])++++#ifdef TEST+stockFile :: B.ByteString+stockFile = B.unlines [+ "# STOCKHOLM 1.0",+ "#=GF AU Infernal 1.0",+ "",+ "#=GS Purine1 DE Number 1 :)",+ "Purine1 AAAAUUGAAUAUCGUUUUACUUGUUUAUGUC-GUGAAU-UGGCAC-GACG",+ "Purine2 AAAAUUUAAUAA-GAAGCACUCAUAUAAUCCCGAGAAUAUGGCUCGGGAG",+ "Purine3 UGGCAGUAACUAGCGUCACUUCGUAUAACCCCAGUGAUAUGGAUUGGGGG",+ "#=GC SS_cons :::::::::::::::::((((((((,,,<<<-<<<_______>>>->>>,",+ "",+ "# We may have comments =)",+ "",+ "Purine1 UUUCUACAAGGUG-CCGGAA--CACCUAACAAUAAGUAAGUCAGCAGUGA",+ "Purine2 UCUCUACCGAACAACCGUAAAUUGUUCGACUAUGAGUGAAAGUGUACCUA",+ "Purine3 UCUCUACCAGGAACCAAUAA--AUCCUGAUUACGAAGAGUUUAGUGCUUU",+ "#=GC SS_cons ,,,,,,,<<<<<<_________>>>>>>,,))))))))::::::::::::",+ "",+ "Purine1 GAU",+ "Purine2 GGG",+ "Purine3 AGU",+ "#=GC SS_cons :::",+ "// "]++purine1, purine2, purine3 :: SeqData+ss_cons :: ByteString+purine1 = SeqData "AAAAUUGAAUAUCGUUUUACUUGUUUAUGUC-GUGAAU-UGGCAC-GACGUUUCUACAAGGUG-CCGGAA--CACCUAACAAUAAGUAAGUCAGCAGUGAGAU"+purine2 = SeqData "AAAAUUUAAUAA-GAAGCACUCAUAUAAUCCCGAGAAUAUGGCUCGGGAGUCUCUACCGAACAACCGUAAAUUGUUCGACUAUGAGUGAAAGUGUACCUAGGG"+purine3 = SeqData "UGGCAGUAACUAGCGUCACUUCGUAUAACCCCAGUGAUAUGGAUUGGGGGUCUCUACCAGGAACCAAUAA--AUCCUGAUUACGAAGAGUUUAGUGCUUUAGU"+ss_cons = ":::::::::::::::::((((((((,,,<<<-<<<_______>>>->>>,,,,,,,,<<<<<<_________>>>>>>,,)))))))):::::::::::::::"++result :: [Stockholm]+result = [Stockholm file clmn seqs]+ where+ file = [Ann AU "Infernal 1.0"]+ clmn = [Ann SS ss_cons]+ seqs = [mkStock "Purine1" purine1 [Ann S_DE "Number 1 :)"],+ mkStock "Purine2" purine2 [],+ mkStock "Purine3" purine3 []]+ mkStock name data_ sa = StSeq name data_ sa []++stockFile2 :: B.ByteString+stockFile2 = B.unlines [stockFile, stockFile]++result2 :: [Stockholm]+result2 = result ++ result++returnExc :: a -> Exceptional B.ByteString a+returnExc = return++test_parseStockholm :: Specs+test_parseStockholm =+ describe "parseStockholm" $ do+ it "correctly parses test file 1" $ parseStockholm stockFile @?= returnExc result+ it "correctly parses test file 2" $ parseStockholm stockFile2 @?= returnExc result2++test_prettyPrintStockholm :: Specs+test_prettyPrintStockholm =+ describe "parseStockholm/prettyPrintStockholm" $ do+ it "parses printed test file 1" $ parseStockholm (func result) @?= returnExc result+ it "parses printed test file 2" $ parseStockholm (func result2) @?= returnExc result2+ where func = B.unlines . map prettyPrintStockholm+#endif+++#ifdef TEST+test_Stockholm :: Specs+test_Stockholm = describe "Bio.Sequence.Stockholm" $ do+ test_parseAnnots+ test_parseStockholm+ test_prettyPrintStockholm+#endif