packages feed

elynx-nexus-0.6.0.0: src/ELynx/Import/Nexus.hs

{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module      :  ELynx.Import.Nexus
-- Description :  Nexus types and classes
-- Copyright   :  (c) Dominik Schrempf 2021
-- License     :  GPL-3
--
-- Maintainer  :  dominik.schrempf@gmail.com
-- Stability   :  unstable
-- Portability :  portable
--
-- Creation date: Tue Apr 28 17:10:05 2020.
module ELynx.Import.Nexus
  ( Block (..),
    nexusBlock,
  )
where

import Control.Applicative
import Data.Attoparsec.ByteString.Char8
import Data.Attoparsec.Combinator
import qualified Data.ByteString.Char8 as BS

-- | A Nexus block has a name (e.g., TREES), and parser for the entry.
data Block a = Block
  { name :: BS.ByteString,
    parser :: Parser a
  }

-- This has to be refined. Like this, only one block can be parsed, and the
-- block type has to be known beforehand.

-- | Parse a given 'Block' in a Nexus file.
--
-- The Nexus file can contain other blocks.
nexusBlock :: Block a -> Parser a
nexusBlock b = do
  start
  _ <- manyTill anyChar (lookAhead $ beginB b) <?> "nexusBlockSkipUntilBlock"
  r <- block b <?> "nexusBlock"
  _ <- many anyChar <?> "nexusBlockSkipUntilEnd"
  _ <- endOfInput
  return r

start :: Parser ()
start = do
  _ <- stringCI "#nexus" <?> "nexusStart"
  skipWhile isSpace
  return ()

block :: Block a -> Parser a
block b = do
  beginB b
  r <- parser b <?> "blockParser"
  endB
  return r

beginB :: Block a -> Parser ()
beginB (Block n _) = do
  _ <- stringCI "begin" <?> "blockBegin"
  skipWhile isSpace
  _ <- stringCI n <?> "blockName"
  _ <- char ';' <?> "blockEnd"
  skipWhile isSpace
  return ()

endB :: Parser ()
endB = do
  _ <- stringCI "end;" <?> "nexusEnd"
  skipWhile isSpace
  return ()