range-0.1.0.0: Data/Range/Parser.hs
{-# 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)