{-# LANGUAGE FlexibleContexts #-}

-- | It should not be unexpected that you will be given a string representation of some
-- ranges and you will need to parse them so that you can then do some further processing.
-- This parser exists in order to make the most common forms of range strings easy to
-- parse. It does not cover all cases however but you should not be too worried about
-- that because you should be able to write your own parser using parsec or Alex/Happy and
-- then you can convert everything that you parse into a RangeTree object for easier
-- processing.
module Data.Range.Parser 
   ( parseRanges
   , ranges
   , RangeParserArgs(..)
   , defaultArgs
   ) where

import Text.Parsec
import Text.Parsec.String

import Data.Range.Range

-- | The arguments that are used, and can be modified, while parsing a standard range
-- string.
data RangeParserArgs = Args 
   { unionSeparator :: String -- ^ A separator that represents a union.
   , rangeSeparator :: String -- ^ A separator that separates the two halves of a range.
   , wildcardSymbol :: String -- ^ A separator that implies an unbounded range.
   }
   deriving(Show)

-- | These are the default arguments that are used by the parser. Please feel free to use
-- the default arguments for you own parser and modify it from the defaults at will.
defaultArgs :: RangeParserArgs 
defaultArgs = Args
   { unionSeparator = ","
   , rangeSeparator = "-"
   , wildcardSymbol = "*"
   }

-- | Given a string this function will either return a parse error back to the user or the
-- list of ranges that are represented by the parsed string.
parseRanges :: (Read a) => String -> Either ParseError [Range a]
parseRanges = parse (ranges defaultArgs) "(range parser)"

string_ :: Stream s m Char => String -> ParsecT s u m ()
string_ x = string x >> return ()

-- | Given the parser arguments this returns a parser that is capable of parsing a list of
-- ranges.
ranges :: (Read a) => RangeParserArgs -> Parser [Range a]
ranges args = range `sepBy` (string $ unionSeparator args)
   where 
      range :: (Read a) => Parser (Range a)
      range = choice 
         [ infiniteRange
         , spanRange
         , singletonRange
         ]

      infiniteRange :: (Read a) => Parser (Range a)
      infiniteRange = do
         string_ $ wildcardSymbol args
         return InfiniteRange

      spanRange :: (Read a) => Parser (Range a)
      spanRange = try $ do
         first <- readSection
         string_ $ rangeSeparator args
         second <- readSection
         case (first, second) of
            (Just x, Just y)  -> return $ SpanRange x y
            (Just x, _)       -> return $ LowerBoundRange x
            (_, Just y)       -> return $ UpperBoundRange y
            _                 -> parserFail ("Range should have a number on one end: " ++ rangeSeparator args)

      singletonRange :: (Read a) => Parser (Range a)
      singletonRange = fmap (SingletonRange . read) $ many1 digit

readSection :: (Read a) => Parser (Maybe a)
readSection = fmap (fmap read) $ optionMaybe (many1 digit)