{-# LANGUAGE FlexibleContexts #-}

module Saturn.Unstable.Type.Range where

import qualified Control.Monad as Monad
import qualified Data.Coerce as Coerce
import qualified Data.Set as Set
import qualified Data.Text.Lazy.Builder as Builder
import qualified Data.Word as Word
import qualified Saturn.Unstable.Extra.Tuple as Tuple
import qualified Saturn.Unstable.Type.Number as Number
import qualified Text.Parsec as Parsec

newtype Range
  = Range (Number.Number, Number.Number)
  deriving (Range -> Range -> Bool
(Range -> Range -> Bool) -> (Range -> Range -> Bool) -> Eq Range
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Range -> Range -> Bool
== :: Range -> Range -> Bool
$c/= :: Range -> Range -> Bool
/= :: Range -> Range -> Bool
Eq, Int -> Range -> ShowS
[Range] -> ShowS
Range -> String
(Int -> Range -> ShowS)
-> (Range -> String) -> ([Range] -> ShowS) -> Show Range
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Range -> ShowS
showsPrec :: Int -> Range -> ShowS
$cshow :: Range -> String
show :: Range -> String
$cshowList :: [Range] -> ShowS
showList :: [Range] -> ShowS
Show)

fromTuple :: (Number.Number, Number.Number) -> Maybe Range
fromTuple :: (Number, Number) -> Maybe Range
fromTuple (Number
lo, Number
hi) =
  if Number -> Word8
Number.toWord8 Number
lo Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
> Number -> Word8
Number.toWord8 Number
hi
    then Maybe Range
forall a. Maybe a
Nothing
    else Range -> Maybe Range
forall a. a -> Maybe a
Just (Range -> Maybe Range) -> Range -> Maybe Range
forall a b. (a -> b) -> a -> b
$ (Number, Number) -> Range
Range (Number
lo, Number
hi)

toTuple :: Range -> (Number.Number, Number.Number)
toTuple :: Range -> (Number, Number)
toTuple = Range -> (Number, Number)
forall a b. Coercible a b => a -> b
Coerce.coerce

parsec :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Range
parsec :: forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Range
parsec = do
  Number
lo <- ParsecT s u m Number
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Number
Number.parsec
  ParsecT s u m Char -> ParsecT s u m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void (ParsecT s u m Char -> ParsecT s u m ())
-> ParsecT s u m Char -> ParsecT s u m ()
forall a b. (a -> b) -> a -> b
$ Char -> ParsecT s u m Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
Parsec.char Char
'-'
  Number
hi <- ParsecT s u m Number
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Number
Number.parsec
  ParsecT s u m Range
-> (Range -> ParsecT s u m Range)
-> Maybe Range
-> ParsecT s u m Range
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> ParsecT s u m Range
forall a. String -> ParsecT s u m a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"invalid Range") Range -> ParsecT s u m Range
forall a. a -> ParsecT s u m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Range -> ParsecT s u m Range)
-> Maybe Range -> ParsecT s u m Range
forall a b. (a -> b) -> a -> b
$ (Number, Number) -> Maybe Range
fromTuple (Number
lo, Number
hi)

toBuilder :: Range -> Builder.Builder
toBuilder :: Range -> Builder
toBuilder Range
range =
  let (Number
lo, Number
hi) = Range -> (Number, Number)
toTuple Range
range
   in Number -> Builder
Number.toBuilder Number
lo Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.singleton Char
'-' Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Number -> Builder
Number.toBuilder Number
hi

isValid :: (Word.Word8, Word.Word8) -> Range -> Bool
isValid :: (Word8, Word8) -> Range -> Bool
isValid (Word8, Word8)
tuple = (Bool -> Bool -> Bool) -> (Bool, Bool) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Bool -> Bool -> Bool
(&&) ((Bool, Bool) -> Bool) -> (Range -> (Bool, Bool)) -> Range -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Number -> Bool) -> (Number, Number) -> (Bool, Bool)
forall a b. (a -> b) -> (a, a) -> (b, b)
Tuple.mapBoth ((Word8, Word8) -> Number -> Bool
Number.isValid (Word8, Word8)
tuple) ((Number, Number) -> (Bool, Bool))
-> (Range -> (Number, Number)) -> Range -> (Bool, Bool)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Range -> (Number, Number)
toTuple

expand :: Range -> Set.Set Word.Word8
expand :: Range -> Set Word8
expand =
  [Word8] -> Set Word8
forall a. Ord a => [a] -> Set a
Set.fromList
    ([Word8] -> Set Word8) -> (Range -> [Word8]) -> Range -> Set Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word8, Word8) -> [Word8]
forall a. Enum a => (a, a) -> [a]
Tuple.toSequence
    ((Word8, Word8) -> [Word8])
-> (Range -> (Word8, Word8)) -> Range -> [Word8]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Number -> Word8) -> (Number, Number) -> (Word8, Word8)
forall a b. (a -> b) -> (a, a) -> (b, b)
Tuple.mapBoth Number -> Word8
Number.toWord8
    ((Number, Number) -> (Word8, Word8))
-> (Range -> (Number, Number)) -> Range -> (Word8, Word8)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Range -> (Number, Number)
toTuple