{-# language LambdaCase #-}
{-# language DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric #-}
module Language.Python.Syntax.CommaSep
( Comma(..)
, CommaSep(..), _CommaSep, csTrailingWhitespace
, appendCommaSep, maybeToCommaSep, listToCommaSep
, CommaSep1(..)
, commaSep1Head, appendCommaSep1, listToCommaSep1, listToCommaSep1'
, CommaSep1'(..)
, _CommaSep1'
)
where
import Control.Lens.Getter ((^.))
import Control.Lens.Iso (Iso, iso)
import Control.Lens.Lens (lens)
import Control.Lens.Setter ((.~))
import Control.Lens.Traversal (Traversal')
import Data.Coerce (coerce)
import Data.Function ((&))
import Data.Functor (($>))
import Data.Functor.Apply ((<.>))
import Data.List.NonEmpty (NonEmpty(..))
import Data.Maybe (fromMaybe)
import Data.Semigroup (Semigroup(..))
import Data.Semigroup.Foldable (Foldable1(..))
import Data.Semigroup.Traversable (Traversable1(..), foldMap1Default)
import GHC.Generics (Generic)
import Language.Python.Syntax.Punctuation
import Language.Python.Syntax.Whitespace (Whitespace (Space), HasTrailingWhitespace (..))
data CommaSep a
= CommaSepNone
| CommaSepOne a
| CommaSepMany a Comma (CommaSep a)
deriving (Eq, Show, Functor, Foldable, Traversable, Generic)
csTrailingWhitespace
:: HasTrailingWhitespace a
=> Traversal' (CommaSep a) [Whitespace]
csTrailingWhitespace _ CommaSepNone = pure CommaSepNone
csTrailingWhitespace f (CommaSepOne a) = CommaSepOne <$> trailingWhitespace f a
csTrailingWhitespace f (CommaSepMany a (MkComma b) CommaSepNone) =
(\b' -> CommaSepMany a (MkComma b') CommaSepNone) <$> f b
csTrailingWhitespace f (CommaSepMany a b c) =
CommaSepMany a b <$> csTrailingWhitespace f c
maybeToCommaSep :: Maybe a -> CommaSep a
maybeToCommaSep = maybe CommaSepNone CommaSepOne
listToCommaSep :: [a] -> CommaSep a
listToCommaSep [] = CommaSepNone
listToCommaSep [a] = CommaSepOne a
listToCommaSep (a:as) = CommaSepMany a (MkComma [Space]) $ listToCommaSep as
appendCommaSep :: [Whitespace] -> CommaSep a -> CommaSep a -> CommaSep a
appendCommaSep _ CommaSepNone b = b
appendCommaSep _ (CommaSepOne a) CommaSepNone = CommaSepOne a
appendCommaSep ws (CommaSepOne a) (CommaSepOne b) = CommaSepMany a (MkComma ws) (CommaSepOne b)
appendCommaSep ws (CommaSepOne a) (CommaSepMany b c cs) = CommaSepMany a (MkComma ws) (CommaSepMany b c cs)
appendCommaSep ws (CommaSepMany a c cs) b = CommaSepMany a c (appendCommaSep ws cs b)
instance Semigroup (CommaSep a) where
(<>) = appendCommaSep [Space]
instance Monoid (CommaSep a) where
mempty = CommaSepNone
mappend = (<>)
data CommaSep1 a
= CommaSepOne1 a
| CommaSepMany1 a Comma (CommaSep1 a)
deriving (Eq, Show, Functor, Foldable, Traversable, Generic)
instance Foldable1 CommaSep1 where; foldMap1 = foldMap1Default
instance Traversable1 CommaSep1 where
traverse1 f = go
where
go (CommaSepOne1 a) = CommaSepOne1 <$> f a
go (CommaSepMany1 a b c) = (\a' c' -> CommaSepMany1 a' b c') <$> f a <.> go c
commaSep1Head :: CommaSep1 a -> a
commaSep1Head (CommaSepOne1 a) = a
commaSep1Head (CommaSepMany1 a _ _) = a
appendCommaSep1 :: [Whitespace] -> CommaSep1 a -> CommaSep1 a -> CommaSep1 a
appendCommaSep1 ws a b =
CommaSepMany1
(case a of; CommaSepOne1 x -> x; CommaSepMany1 x _ _ -> x)
(case a of; CommaSepOne1 _ -> MkComma ws; CommaSepMany1 _ ws' _ -> ws')
(case a of; CommaSepOne1 _ -> b; CommaSepMany1 _ _ x -> x <> b)
instance Semigroup (CommaSep1 a) where
(<>) = appendCommaSep1 [Space]
instance HasTrailingWhitespace s => HasTrailingWhitespace (CommaSep1 s) where
trailingWhitespace =
lens
(\case
CommaSepOne1 a -> a ^. trailingWhitespace
CommaSepMany1 _ _ a -> a ^. trailingWhitespace)
(\cs ws ->
case cs of
CommaSepOne1 a ->
CommaSepOne1 (a & trailingWhitespace .~ ws)
CommaSepMany1 a b c -> CommaSepMany1 (coerce a) b (c & trailingWhitespace .~ ws))
listToCommaSep1 :: NonEmpty a -> CommaSep1 a
listToCommaSep1 (a :| as) = go (a:as)
where
go [] = error "impossible"
go [x] = CommaSepOne1 x
go (x:xs) = CommaSepMany1 x (MkComma [Space]) $ go xs
data CommaSep1' a
= CommaSepOne1' a (Maybe Comma)
| CommaSepMany1' a Comma (CommaSep1' a)
deriving (Eq, Show, Functor, Foldable, Traversable, Generic)
instance Foldable1 CommaSep1' where; foldMap1 = foldMap1Default
instance Traversable1 CommaSep1' where
traverse1 f = go
where
go (CommaSepOne1' a b) = (\a' -> CommaSepOne1' a' b) <$> f a
go (CommaSepMany1' a b c) = (\a' c' -> CommaSepMany1' a' b c') <$> f a <.> go c
_CommaSep
:: Iso
(Maybe (a, [(Comma, a)], Maybe Comma))
(Maybe (b, [(Comma, b)], Maybe Comma))
(CommaSep a)
(CommaSep b)
_CommaSep = iso toCs fromCs
where
toCs :: Maybe (a, [(Comma, a)], Maybe Comma) -> CommaSep a
toCs Nothing = CommaSepNone
toCs (Just (a, b, c)) =
case b of
[] -> maybe (CommaSepOne a) (\c' -> CommaSepMany a c' CommaSepNone) c
(d, e):ds -> CommaSepMany a d $ toCs (Just (e, ds, c))
fromCs :: CommaSep a -> Maybe (a, [(Comma, a)], Maybe Comma)
fromCs CommaSepNone = Nothing
fromCs (CommaSepOne a) = Just (a, [], Nothing)
fromCs (CommaSepMany a b c) =
case fromCs c of
Nothing -> Just (a, [], Just b)
Just (x, y, z) -> Just (a, (b, x) : y, z)
_CommaSep1'
:: Iso
(a, [(Comma, a)], Maybe Comma)
(b, [(Comma, b)], Maybe Comma)
(CommaSep1' a)
(CommaSep1' b)
_CommaSep1' = iso toCs fromCs
where
toCs (a, [], b) = CommaSepOne1' a b
toCs (a, (b, c) : bs, d) = CommaSepMany1' a b $ toCs (c, bs, d)
fromCs (CommaSepOne1' a b) = (a, [], b)
fromCs (CommaSepMany1' a b c) =
let
(d, e, f) = fromCs c
in
(a, (b, d) : e, f)
listToCommaSep1' :: [a] -> Maybe (CommaSep1' a)
listToCommaSep1' [] = Nothing
listToCommaSep1' [a] = Just (CommaSepOne1' a Nothing)
listToCommaSep1' (a:as) =
CommaSepMany1' a (MkComma [Space]) <$> listToCommaSep1' as
instance HasTrailingWhitespace s => HasTrailingWhitespace (CommaSep1' s) where
trailingWhitespace =
lens
(\case
CommaSepOne1' a b -> maybe (a ^. trailingWhitespace) (^. trailingWhitespace) b
CommaSepMany1' _ _ a -> a ^. trailingWhitespace)
(\cs ws ->
case cs of
CommaSepOne1' a b ->
CommaSepOne1'
(fromMaybe (a & trailingWhitespace .~ ws) $ b $> coerce a)
(b $> MkComma ws)
CommaSepMany1' a b c ->
CommaSepMany1' (coerce a) b (c & trailingWhitespace .~ ws))