packages feed

kleene 0 → 0.1

raw patch · 16 files changed

+1615/−958 lines, 16 filesdep +attoparsecdep +base-compatdep +bytestringdep −base-compat-batteriesdep ~QuickCheckdep ~basedep ~containersbinary-addedPVP ok

version bump matches the API change (PVP)

Dependencies added: attoparsec, base-compat, bytestring, semigroupoids, semigroups

Dependencies removed: base-compat-batteries

Dependency ranges changed: QuickCheck, base, containers, lattices, text, transformers

API changes (from Hackage documentation)

- Kleene.ERE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c) => Algebra.Lattice.BoundedLattice (Kleene.ERE.ERE c)
- Kleene.ERE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c) => Algebra.Lattice.JoinSemiLattice (Kleene.ERE.ERE c)
- Kleene.ERE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c) => Algebra.Lattice.MeetSemiLattice (Kleene.ERE.ERE c)
- Kleene.ERE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.Kleene c (Kleene.ERE.ERE c)
- Kleene.ERE: instance GHC.Classes.Eq c => Data.Semigroup.Semigroup (Kleene.ERE.ERE c)
- Kleene.ERE: instance c ~ GHC.Types.Char => Data.String.IsString (Kleene.ERE.ERE c)
- Kleene.ERE: instance c ~ GHC.Types.Char => Kleene.Internal.Pretty.Pretty (Kleene.ERE.ERE c)
- Kleene.Equiv: instance (Algebra.Lattice.JoinSemiLattice (r c), Kleene.Classes.Equivalent c (r c)) => Algebra.PartialOrd.PartialOrd (Kleene.Equiv.Equiv r c)
- Kleene.Equiv: instance Algebra.Lattice.JoinSemiLattice (r c) => Algebra.Lattice.JoinSemiLattice (Kleene.Equiv.Equiv r c)
- Kleene.Equiv: instance Data.Semigroup.Semigroup (r c) => Data.Semigroup.Semigroup (Kleene.Equiv.Equiv r c)
- Kleene.Equiv: instance Kleene.Classes.Kleene c (r c) => Kleene.Classes.Kleene c (Kleene.Equiv.Equiv r c)
- Kleene.Functor: instance (c ~ GHC.Types.Char, Data.String.IsString a) => Data.String.IsString (Kleene.Functor.K c a)
- Kleene.Functor: instance GHC.Base.Alternative (Kleene.Functor.K c)
- Kleene.Functor: instance GHC.Base.Applicative (Kleene.Functor.K c)
- Kleene.Functor: instance GHC.Base.Functor (Kleene.Functor.K c)
- Kleene.Functor: instance GHC.Classes.Eq Kleene.Functor.Greediness
- Kleene.Functor: instance GHC.Classes.Ord Kleene.Functor.Greediness
- Kleene.Functor: instance GHC.Enum.Bounded Kleene.Functor.Greediness
- Kleene.Functor: instance GHC.Enum.Enum Kleene.Functor.Greediness
- Kleene.Functor: instance GHC.Show.Show Kleene.Functor.Greediness
- Kleene.Functor: instance c ~ GHC.Types.Char => Kleene.Internal.Pretty.Pretty (Kleene.Functor.K c a)
- Kleene.Internal.Partition: instance (GHC.Enum.Enum a, GHC.Enum.Bounded a, GHC.Classes.Ord a) => Data.Semigroup.Semigroup (Kleene.Internal.Partition.Partition a)
- Kleene.Internal.Pretty: instance c ~ GHC.Types.Char => Kleene.Internal.Pretty.Pretty (Data.RangeSet.Map.RSet c)
- Kleene.Monad: instance Algebra.Lattice.BoundedJoinSemiLattice (Kleene.Monad.M c)
- Kleene.Monad: instance Algebra.Lattice.JoinSemiLattice (Kleene.Monad.M c)
- Kleene.Monad: instance Data.Semigroup.Semigroup (Kleene.Monad.M c)
- Kleene.Monad: instance Kleene.Classes.Kleene c (Kleene.Monad.M c)
- Kleene.Monad: instance c ~ GHC.Types.Char => Data.String.IsString (Kleene.Monad.M c)
- Kleene.RE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Algebra.Lattice.BoundedJoinSemiLattice (Kleene.RE.RE c)
- Kleene.RE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Algebra.Lattice.JoinSemiLattice (Kleene.RE.RE c)
- Kleene.RE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.Derivate c (Kleene.RE.RE c)
- Kleene.RE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.Equivalent c (Kleene.RE.RE c)
- Kleene.RE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.FiniteKleene c (Kleene.RE.RE c)
- Kleene.RE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.Kleene c (Kleene.RE.RE c)
- Kleene.RE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.Match c (Kleene.RE.RE c)
- Kleene.RE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.TransitionMap c (Kleene.RE.RE c)
- Kleene.RE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c, Test.QuickCheck.Arbitrary.Arbitrary c) => Test.QuickCheck.Arbitrary.Arbitrary (Kleene.RE.RE c)
- Kleene.RE: instance GHC.Classes.Eq c => Data.Semigroup.Semigroup (Kleene.RE.RE c)
- Kleene.RE: instance GHC.Classes.Eq c => GHC.Base.Monoid (Kleene.RE.RE c)
- Kleene.RE: instance GHC.Classes.Eq c => GHC.Classes.Eq (Kleene.RE.RE c)
- Kleene.RE: instance GHC.Classes.Ord c => GHC.Classes.Ord (Kleene.RE.RE c)
- Kleene.RE: instance GHC.Show.Show c => GHC.Show.Show (Kleene.RE.RE c)
- Kleene.RE: instance Test.QuickCheck.Arbitrary.CoArbitrary c => Test.QuickCheck.Arbitrary.CoArbitrary (Kleene.RE.RE c)
- Kleene.RE: instance c ~ GHC.Types.Char => Data.String.IsString (Kleene.RE.RE c)
- Kleene.RE: instance c ~ GHC.Types.Char => Kleene.Internal.Pretty.Pretty (Kleene.RE.RE c)
+ Kleene: [dfaInitial] :: DFA c -> !Int
+ Kleene: anyChar :: FiniteKleene c k => k
+ Kleene: charRange :: FiniteKleene c k => c -> c -> k
+ Kleene: class Kleene k => CharKleene c k | k -> c
+ Kleene: class CharKleene c k => FiniteKleene c k | k -> c
+ Kleene: class ToLatin1 k
+ Kleene: dot :: (FiniteKleene c k, c ~ Char) => k
+ Kleene: everything :: FiniteKleene c k => k
+ Kleene: fromRSet :: FiniteKleene c k => RSet c -> k
+ Kleene: match8 :: (Match c k, c ~ Word8) => k -> ByteString -> Bool
+ Kleene: notChar :: (FiniteKleene c k, Ord c, Enum c, Bounded c) => c -> k
+ Kleene: string :: CharKleene c k => [c] -> k
+ Kleene: toDot :: DFA Char -> String
+ Kleene: toLatin1 :: ToLatin1 k => k Char -> k Word8
+ Kleene.Classes: class Kleene k => CharKleene c k | k -> c
+ Kleene.Classes: class ToLatin1 k
+ Kleene.Classes: instance Kleene.Classes.ToLatin1 Data.RangeSet.Map.RSet
+ Kleene.Classes: match8 :: (Match c k, c ~ Word8) => k -> ByteString -> Bool
+ Kleene.Classes: notChar :: (FiniteKleene c k, Ord c, Enum c, Bounded c) => c -> k
+ Kleene.Classes: string :: CharKleene c k => [c] -> k
+ Kleene.Classes: toLatin1 :: ToLatin1 k => k Char -> k Word8
+ Kleene.DFA: [dfaInitial] :: DFA c -> !Int
+ Kleene.DFA: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Algebra.Lattice.BoundedJoinSemiLattice (Kleene.Internal.RE.RE c)
+ Kleene.DFA: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Algebra.Lattice.BoundedMeetSemiLattice (Kleene.Internal.RE.RE c)
+ Kleene.DFA: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Algebra.Lattice.Lattice (Kleene.Internal.RE.RE c)
+ Kleene.DFA: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.Complement c (Kleene.Internal.RE.RE c)
+ Kleene.DFA: instance GHC.Classes.Ord c => Kleene.Classes.Derivate c (Kleene.DFA.DFA c)
+ Kleene.DFA: toDot :: DFA Char -> String
+ Kleene.DFA: toDot' :: (Ord c, Enum c, Bounded c) => (Int -> String) -> (c -> String) -> DFA c -> String
+ Kleene.ERE: fromRE :: Ord c => RE c -> ERE c
+ Kleene.ERE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.CharKleene c (Kleene.ERE.ERE c)
+ Kleene.ERE: instance (GHC.Classes.Ord c, GHC.Enum.Enum c, GHC.Enum.Bounded c) => Kleene.Classes.Kleene (Kleene.ERE.ERE c)
+ Kleene.ERE: instance (c Data.Type.Equality.~ GHC.Types.Char) => Data.String.IsString (Kleene.ERE.ERE c)
+ Kleene.ERE: instance (c Data.Type.Equality.~ GHC.Types.Char) => Kleene.Internal.Pretty.Pretty (Kleene.ERE.ERE c)
+ Kleene.ERE: instance GHC.Classes.Eq c => GHC.Base.Semigroup (Kleene.ERE.ERE c)
+ Kleene.ERE: instance Kleene.Classes.ToLatin1 Kleene.ERE.ERE
+ Kleene.Equiv: instance (Algebra.Lattice.Lattice (r c), Kleene.Classes.Equivalent c (r c)) => Algebra.PartialOrd.PartialOrd (Kleene.Equiv.Equiv r c)
+ Kleene.Equiv: instance Algebra.Lattice.BoundedMeetSemiLattice (r c) => Algebra.Lattice.BoundedMeetSemiLattice (Kleene.Equiv.Equiv r c)
+ Kleene.Equiv: instance Algebra.Lattice.Lattice (r c) => Algebra.Lattice.Lattice (Kleene.Equiv.Equiv r c)
+ Kleene.Equiv: instance GHC.Base.Semigroup (r c) => GHC.Base.Semigroup (Kleene.Equiv.Equiv r c)
+ Kleene.Equiv: instance Kleene.Classes.CharKleene c (r c) => Kleene.Classes.CharKleene c (Kleene.Equiv.Equiv r c)
+ Kleene.Equiv: instance Kleene.Classes.Kleene (r c) => Kleene.Classes.Kleene (Kleene.Equiv.Equiv r c)
+ Kleene.Functor.NonEmpty: Greedy :: Greediness
+ Kleene.Functor.NonEmpty: NonGreedy :: Greediness
+ Kleene.Functor.NonEmpty: anyChar :: (Ord c, Enum c, Bounded c) => K1 c c
+ Kleene.Functor.NonEmpty: char :: (Ord c, Enum c) => c -> K1 c c
+ Kleene.Functor.NonEmpty: charRange :: (Enum c, Ord c) => c -> c -> K1 c c
+ Kleene.Functor.NonEmpty: data Greediness
+ Kleene.Functor.NonEmpty: data K1 c a
+ Kleene.Functor.NonEmpty: dot :: K1 Char Char
+ Kleene.Functor.NonEmpty: everything1 :: (Ord c, Enum c, Bounded c) => K1 c (NonEmpty c)
+ Kleene.Functor.NonEmpty: instance (c Data.Type.Equality.~ GHC.Types.Char) => Kleene.Internal.Pretty.Pretty (Kleene.Functor.NonEmpty.K1 c a)
+ Kleene.Functor.NonEmpty: instance Data.Functor.Alt.Alt (Kleene.Functor.NonEmpty.K1 c)
+ Kleene.Functor.NonEmpty: instance Data.Functor.Bind.Class.Apply (Kleene.Functor.NonEmpty.K1 c)
+ Kleene.Functor.NonEmpty: instance GHC.Base.Functor (Kleene.Functor.NonEmpty.K1 c)
+ Kleene.Functor.NonEmpty: isEmpty :: (Ord c, Enum c, Bounded c) => K1 c a -> Bool
+ Kleene.Functor.NonEmpty: isEverything :: (Ord c, Enum c, Bounded c) => K1 c a -> Bool
+ Kleene.Functor.NonEmpty: match :: K1 c a -> [c] -> Maybe a
+ Kleene.Functor.NonEmpty: nullableProof :: K c a -> Either (K1 c a) (a, K1 c a)
+ Kleene.Functor.NonEmpty: oneof :: (Ord c, Enum c, Foldable f) => f c -> K1 c c
+ Kleene.Functor.NonEmpty: some1 :: K1 c a -> K1 c (NonEmpty a)
+ Kleene.Functor.NonEmpty: string :: String -> K1 Char (NonEmpty Char)
+ Kleene.Functor.NonEmpty: toKleene :: FiniteKleene c k => K1 c a -> k
+ Kleene.Functor.NonEmpty: toRA :: K1 c a -> RE c a
+ Kleene.Functor.NonEmpty: toRE :: (Ord c, Enum c, Bounded c) => K1 c a -> RE c
+ Kleene.Internal.Partition: instance (GHC.Enum.Enum a, GHC.Enum.Bounded a, GHC.Classes.Ord a) => GHC.Base.Semigroup (Kleene.Internal.Partition.Partition a)
+ Kleene.Internal.Pretty: instance (Kleene.Internal.Pretty.Pretty a, Kleene.Internal.Pretty.Pretty b) => Kleene.Internal.Pretty.Pretty (Data.Either.Either a b)
+ Kleene.Internal.Pretty: instance (Kleene.Internal.Pretty.Pretty a, Kleene.Internal.Pretty.Pretty b) => Kleene.Internal.Pretty.Pretty (a, b)
+ Kleene.Internal.Pretty: instance (c Data.Type.Equality.~ GHC.Types.Char) => Kleene.Internal.Pretty.Pretty (Data.RangeSet.Map.RSet c)
+ Kleene.Internal.Pretty: instance GHC.Show.Show a => Kleene.Internal.Pretty.Pretty [a]
+ Kleene.Internal.Pretty: instance Kleene.Internal.Pretty.Pretty a => Kleene.Internal.Pretty.Pretty (GHC.Maybe.Maybe a)
+ Kleene.Monad: instance (c Data.Type.Equality.~ GHC.Types.Char) => Data.String.IsString (Kleene.Monad.M c)
+ Kleene.Monad: instance GHC.Base.Semigroup (Kleene.Monad.M c)
+ Kleene.Monad: instance Kleene.Classes.CharKleene c (Kleene.Monad.M c)
+ Kleene.Monad: instance Kleene.Classes.Kleene (Kleene.Monad.M c)
+ Kleene.RE: nullableProof :: forall c. (Ord c, Enum c, Bounded c) => RE c -> Maybe (RE c)
- Kleene: DFA :: !(IntMap (SF c Int)) -> !IntSet -> !IntSet -> DFA c
+ Kleene: DFA :: !IntMap (SF c Int) -> !Int -> !IntSet -> !IntSet -> DFA c
- Kleene: Equiv :: (r c) -> Equiv r c
+ Kleene: Equiv :: r c -> Equiv r c
- Kleene: [dfaTransition] :: DFA c -> !(IntMap (SF c Int))
+ Kleene: [dfaTransition] :: DFA c -> !IntMap (SF c Int)
- Kleene: appends :: Kleene c k => [k] -> k
+ Kleene: appends :: Kleene k => [k] -> k
- Kleene: char :: Kleene c k => c -> k
+ Kleene: char :: CharKleene c k => c -> k
- Kleene: class (BoundedJoinSemiLattice k, Semigroup k, Monoid k) => Kleene c k | k -> c
+ Kleene: class Kleene k
- Kleene: empty :: Kleene c k => k
+ Kleene: empty :: Kleene k => k
- Kleene: eps :: Kleene c k => k
+ Kleene: eps :: Kleene k => k
- Kleene: star :: Kleene c k => k -> k
+ Kleene: star :: Kleene k => k -> k
- Kleene: unions :: Kleene c k => [k] -> k
+ Kleene: unions :: Kleene k => [k] -> k
- Kleene.Classes: appends :: Kleene c k => [k] -> k
+ Kleene.Classes: appends :: Kleene k => [k] -> k
- Kleene.Classes: char :: Kleene c k => c -> k
+ Kleene.Classes: char :: CharKleene c k => c -> k
- Kleene.Classes: class Kleene c k => FiniteKleene c k | k -> c
+ Kleene.Classes: class CharKleene c k => FiniteKleene c k | k -> c
- Kleene.Classes: class (BoundedJoinSemiLattice k, Semigroup k, Monoid k) => Kleene c k | k -> c
+ Kleene.Classes: class Kleene k
- Kleene.Classes: empty :: Kleene c k => k
+ Kleene.Classes: empty :: Kleene k => k
- Kleene.Classes: eps :: Kleene c k => k
+ Kleene.Classes: eps :: Kleene k => k
- Kleene.Classes: oneof :: (Kleene c k, Foldable f) => f c -> k
+ Kleene.Classes: oneof :: (CharKleene c k, Foldable f) => f c -> k
- Kleene.Classes: star :: Kleene c k => k -> k
+ Kleene.Classes: star :: Kleene k => k -> k
- Kleene.Classes: unions :: Kleene c k => [k] -> k
+ Kleene.Classes: unions :: Kleene k => [k] -> k
- Kleene.DFA: DFA :: !(IntMap (SF c Int)) -> !IntSet -> !IntSet -> DFA c
+ Kleene.DFA: DFA :: !IntMap (SF c Int) -> !Int -> !IntSet -> !IntSet -> DFA c
- Kleene.DFA: [dfaTransition] :: DFA c -> !(IntMap (SF c Int))
+ Kleene.DFA: [dfaTransition] :: DFA c -> !IntMap (SF c Int)
- Kleene.DFA: toERE :: forall c. (Ord c, Enum c, Bounded c) => DFA c -> ERE c
+ Kleene.DFA: toERE :: (Ord c, Enum c, Bounded c) => DFA c -> ERE c
- Kleene.DFA: toRE :: forall c. (Ord c, Enum c, Bounded c) => DFA c -> RE c
+ Kleene.DFA: toRE :: (Ord c, Enum c, Bounded c) => DFA c -> RE c
- Kleene.ERE: EREChars :: (RSet c) -> ERE c
+ Kleene.ERE: EREChars :: RSet c -> ERE c
- Kleene.ERE: ERENot :: (ERE c) -> ERE c
+ Kleene.ERE: ERENot :: ERE c -> ERE c
- Kleene.ERE: EREStar :: (ERE c) -> ERE c
+ Kleene.ERE: EREStar :: ERE c -> ERE c
- Kleene.ERE: EREUnion :: (RSet c) -> (Set (ERE c)) -> ERE c
+ Kleene.ERE: EREUnion :: RSet c -> Set (ERE c) -> ERE c
- Kleene.Equiv: Equiv :: (r c) -> Equiv r c
+ Kleene.Equiv: Equiv :: r c -> Equiv r c
- Kleene.Monad: MStar :: (M c) -> M c
+ Kleene.Monad: MStar :: M c -> M c
- Kleene.Monad: toKleene :: Kleene c k => M c -> k
+ Kleene.Monad: toKleene :: CharKleene c k => M c -> k
- Kleene.RE: REChars :: (RSet c) -> RE c
+ Kleene.RE: REChars :: RSet c -> RE c
- Kleene.RE: REStar :: (RE c) -> RE c
+ Kleene.RE: REStar :: RE c -> RE c
- Kleene.RE: REUnion :: (RSet c) -> (Set (RE c)) -> RE c
+ Kleene.RE: REUnion :: RSet c -> Set (RE c) -> RE c

Files

+ CHANGELOG.md view
@@ -0,0 +1,11 @@+## 0.1++* Drop superclasses from `Kleene`.+* Rearrange classes. Introduce `CharKleene`, `FiniteKleene`.+* Add `ToLatin1` and ability to match on `ByteString`.+* Add `Derivate c (DFA c)` instance.+* Add `toDot` to output `DFA` to be rendered by *graphviz*.+* Add `fromRE :: RE c -> ERE c`+* Add `nullableProof :: RE c -> Maybe (RE c)` which returns non-nullable part+  of given regular expression.+* Support/require `lattices-2`: `RE` is now a `Lattice`, `M` isn't.
+ example.png view

binary file changed (absent → 24468 bytes)

kleene.cabal view
@@ -1,9 +1,8 @@-cabal-version:  2.0-name:           kleene-version:        0--synopsis:       Kleene algebra-category:       Math+cabal-version:      1.24+name:               kleene+version:            0.1+synopsis:           Kleene algebra+category:           Math description:   Kleene algebra   .@@ -13,53 +12,57 @@   Scott Owens, John Reppy and Aaron Turon   <https://doi.org/10.1017/S0956796808007090> -homepage:       https://github.com/phadej/kleene-bug-reports:    https://github.com/phadej/kleene/issues-author:         Oleg Grenrus <oleg.grenrus@iki.fi>-maintainer:     Oleg Grenrus <oleg.grenrus@iki.fi>-license:        BSD3-license-file:   LICENSE-build-type:     Simple-+homepage:           https://github.com/phadej/kleene+bug-reports:        https://github.com/phadej/kleene/issues+author:             Oleg Grenrus <oleg.grenrus@iki.fi>+maintainer:         Oleg Grenrus <oleg.grenrus@iki.fi>+license:            BSD3+license-file:       LICENSE+build-type:         Simple+extra-source-files: CHANGELOG.md+extra-doc-files:    example.png tested-with:-  GHC ==7.8.4-   || ==7.10.3-   || ==8.0.2-   || ==8.2.2-   || ==8.4.2+  GHC ==7.8.4 || ==7.10.3 || ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1  source-repository head-  type: git+  type:     git   location: https://github.com/phadej/kleene  library   -- GHC boot libraries   build-depends:-    base                  >=4.7.0.2 && <4.12,-    containers            >=0.5.5.1 && <0.6,-    text                  >=1.2.3.0 && <1.3,-    transformers          >=0.3.0.0 && <0.6+      base          >=4.7.0.2  && <4.13+    , bytestring    >=0.10.4.0 && <0.11+    , containers    >=0.5.5.1  && <0.7+    , text          >=1.2.3.0  && <1.3+    , transformers  >=0.3.0.0  && <0.6 +  if !impl(ghc >=8.0)+    build-depends: semigroups >=0.18.5 && <0.19+   -- Other dependencies   build-depends:-    base-compat-batteries >=0.10.1  && <0.11,-    lattices              >=1.7.1   && <1.8,-    MemoTrie              >=0.6.9   && <0.7,-    range-set-list        >=0.1.3   && <0.2,-    step-function         >=0.2     && <0.3,-    regex-applicative     >=0.3.3   && <0.4,-    QuickCheck            >=2.11.3  && <2.12+      attoparsec+    , base-compat        >=0.10.5   && <0.11+    , lattices           >=2        && <2.1+    , MemoTrie           >=0.6.9    && <0.7+    , QuickCheck         >=2.12.6.1 && <2.13+    , range-set-list     >=0.1.3    && <0.2+    , regex-applicative  >=0.3.3    && <0.4+    , semigroupoids      >=5.3.2    && <5.4+    , step-function      >=0.2      && <0.3    other-extensions:     CPP-    DeriveFunctor+    DefaultSignatures     DeriveFoldable+    DeriveFunctor     DeriveTraversable-    GADTs-    OverloadedStrings     FlexibleInstances     FunctionalDependencies+    GADTs     GeneralizedNewtypeDeriving+    OverloadedStrings     StandaloneDeriving     UndecidableInstances @@ -70,6 +73,7 @@     Kleene.ERE     Kleene.Equiv     Kleene.Functor+    Kleene.Functor.NonEmpty     Kleene.Monad     Kleene.RE @@ -79,6 +83,10 @@     Kleene.Internal.Pretty     Kleene.Internal.Sets -  ghc-options: -Wall-  hs-source-dirs: src+  other-modules:+    Kleene.Internal.Functor+    Kleene.Internal.RE++  ghc-options:      -Wall+  hs-source-dirs:   src   default-language: Haskell2010
src/Kleene.hs view
@@ -25,7 +25,8 @@ -- -- We can convert it to 'DFA' (there are 8 states) ----- >>> putPretty $ fromTM re+-- >>> let dfa = fromTM re+-- >>> putPretty dfa -- 0 -> \x -> if --     | x <= '`'  -> 8 --     | x <= 'a'  -> 5@@ -40,9 +41,18 @@ -- 2 -> ... -- ... --+-- It's also possible to graphically visualise DFAs+--+-- @+-- λ> writeFile "example.dot' ('toDot' dfa)+-- %  dot -Tpng -oexample.png example.dot+-- @+--+-- ![example.png](example.png)+-- -- And we can convert back from 'DFA' to 'RE': ----- >>> let re' = toKleene (fromTM re) :: RE Char+-- >>> let re' = toKleene dfa :: RE Char -- >>> putPretty re' -- ^(a(bca)*bcdefx|defx|(a(bca)*bcdefy|defy)z)$ --@@ -134,7 +144,7 @@     RE,     ERE, -    -- * Equivalance (and partial order)+    -- * Equivalence (and partial order)     Equiv (..),      -- * Deterministic finite automaton@@ -142,6 +152,7 @@     fromTM,     fromTMEquiv,     toKleene,+    toDot,      -- * Classes     --@@ -149,10 +160,13 @@     --     -- See "Kleene.RE" module for a specific version with examples.     Kleene (..),+    CharKleene (..),+    FiniteKleene (..),     Derivate (..),     Match (..),     TransitionMap (..),     Complement (..),+    ToLatin1 (..),      -- * Functor     --@@ -163,7 +177,7 @@     ) where  import Kleene.Classes-import Kleene.DFA     (DFA (..), fromTM, fromTMEquiv, toKleene)+import Kleene.DFA     (DFA (..), fromTM, fromTMEquiv, toDot, toKleene) import Kleene.Equiv import Kleene.ERE     (ERE) import Kleene.Functor (K)
src/Kleene/Classes.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DefaultSignatures      #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs                  #-} module Kleene.Classes where@@ -5,42 +6,54 @@ import Prelude () import Prelude.Compat -import Algebra.Lattice                    (BoundedJoinSemiLattice (..), joins)+import Data.Char                          (ord) import Data.Foldable                      (toList) import Data.Function.Step.Discrete.Closed (SF) import Data.Map                           (Map)+import Data.Maybe                         (mapMaybe) import Data.RangeSet.Map                  (RSet)+import Data.Word                          (Word8) +import qualified Data.ByteString   as BS+import qualified Data.RangeSet.Map as RSet+ import Kleene.Internal.Sets (dotRSet) -class (BoundedJoinSemiLattice k, Semigroup k, Monoid k) => Kleene c k | k -> c where+-- | Kleene algebra.+--+-- If 'k' is 'Monoid' it's expected that @'appends' = 'mappend'@;+-- if 'k' is 'Algebra.Lattice.Lattice' it's expected that @'unions' = 'Algebra.Lattice.joins'@.+--+-- [Wikipedia: Kleene Algebra](https://en.wikipedia.org/wiki/Kleene_algebra).+--+class Kleene k where     -- | Empty regex. Doesn't accept anything.     empty :: k-    empty = bottom -    -- | Empty string. /Note:/ different than 'empty'+    -- | Empty string. /Note:/ different than 'empty'.     eps :: k-    eps = mempty -    -- | Single character-    char :: c -> k-     -- | Concatenation.     appends :: [k] -> k-    appends = mconcat      -- | Union.     unions :: [k] -> k-    unions = joins -    -- | Kleene star+    -- | Kleene star.     star :: k -> k +class Kleene k => CharKleene c k | k -> c where+    -- | Single character+    char :: c -> k++    string :: [c] -> k+    string = appends . map char+ -- | One of the characters.-oneof :: (Kleene c k, Foldable f) => f c -> k+oneof :: (CharKleene c k, Foldable f) => f c -> k oneof = unions . map char . toList -class Kleene c k => FiniteKleene c k | k -> c where+class CharKleene c k => FiniteKleene c k | k -> c where     -- | Everything. \(\Sigma^\star\).     everything :: k     everything = star anyChar@@ -51,13 +64,17 @@     -- | Generalisation of 'charRange'.     fromRSet :: RSet c -> k -    -- | @.$. Every character except new line @\\n@.+    -- | @.@ Every character except new line @\\n@.     dot :: c ~ Char => k     dot = fromRSet dotRSet -    -- | Any character. /Note:/ different than dot!+    -- | Any character. /Note:/ different than 'dot'!     anyChar :: k +    notChar :: c -> k+    default notChar :: (Ord c, Enum c, Bounded c) => c -> k+    notChar = fromRSet . RSet.complement . RSet.singleton+ class Derivate c k | k -> c where     -- | Does language contain an empty string?     nullable :: k -> Bool@@ -69,12 +86,15 @@ class Match c k | k -> c where     match :: k -> [c] -> Bool --- | Equivalence induced by 'Matches'.+    match8 :: c ~ Word8 => k -> BS.ByteString -> Bool+    match8 k = match k . BS.unpack++-- | Equivalence induced by 'Match'. -- -- /Law:/ -- -- @--- 'equivalent' re1 re2 <=> forall s. 'matches' re1 s == 'matches' re1 s+-- 'equivalent' re1 re2 <=> forall s. 'match' re1 s == 'match' re1 s -- @ -- class Match c k => Equivalent c k | k -> c  where@@ -89,7 +109,17 @@ -- /Law:/ -- -- @--- 'matches' ('complement' f) xs = 'not' ('matches' f) xs+-- 'match' ('complement' f) xs = 'not' ('match' f) xs -- @ class Complement c k | k -> c where     complement :: k -> k++class ToLatin1 k where+    toLatin1 :: k Char -> k Word8++instance ToLatin1 RSet where+    toLatin1 = RSet.fromRangeList . mapMaybe f . RSet.toRangeList where+        f :: (Char, Char) -> Maybe (Word8, Word8)+        f (a, b)+            | ord a >= 256 = Nothing+            | otherwise    = Just (fromIntegral (ord a), fromIntegral (min 255 (ord b)))
src/Kleene/DFA.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE GADTs                  #-} {-# LANGUAGE Safe                   #-} {-# LANGUAGE ScopedTypeVariables    #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} module Kleene.DFA (     DFA (..),     -- * Conversions@@ -14,12 +15,15 @@     fromTM,     fromTMEquiv,     toKleene,+    toDot,+    toDot',     ) where  import Prelude () import Prelude.Compat -import Algebra.Lattice   ((\/))+import Algebra.Lattice+       (BoundedJoinSemiLattice (..), BoundedMeetSemiLattice (..), Lattice (..)) import Data.IntMap       (IntMap) import Data.IntSet       (IntSet) import Data.List         (intercalate)@@ -27,9 +31,10 @@ import Data.Maybe        (fromMaybe) import Data.RangeSet.Map (RSet) +import qualified Data.ByteString                    as BS import qualified Data.Function.Step.Discrete.Closed as SF-import qualified Data.IntMap                        as IM-import qualified Data.IntSet                        as IS+import qualified Data.IntMap                        as IntMap+import qualified Data.IntSet                        as IntSet import qualified Data.Map                           as Map import qualified Data.MemoTrie                      as MT import qualified Data.RangeSet.Map                  as RSet@@ -37,15 +42,15 @@ import           Kleene.Classes import qualified Kleene.ERE             as ERE import           Kleene.Internal.Pretty-import qualified Kleene.RE              as RE+import qualified Kleene.Internal.RE     as RE  -- | Deterministic finite automaton. -- -- A deterministic finite automaton (DFA) over an alphabet \(\Sigma\) (type -- variable @c@) is 4-tuple \(Q\), \(q_0\) , \(F\), \(\delta\), where ----- * \(Q\) is a finite set of states (subset of 'Int'),--- * \(q_0 \in Q\) is the distinguised start state (@0@),+-- * \(Q\) is a finite set of states (subset of 's'),+-- * \(q_0 \in Q\) is the distinguised start state ('dfaInitial'), -- * \(F \subset Q\) is a set of final (or  accepting) states ('dfaAcceptable'), and -- * \(\delta : Q \times \Sigma \to Q\) is a function called the state -- transition function ('dfaTransition').@@ -53,6 +58,8 @@ data DFA c = DFA     { dfaTransition   :: !(IntMap (SF.SF c Int))       -- ^ transition function+    , dfaInitial      :: !Int+      -- ^ initial state     , dfaAcceptable   :: !IntSet       -- ^ accept states     , dfaBlackholes   :: !IntSet@@ -112,15 +119,15 @@  -- | Convert 'ERE.ERE' to 'DFA'. ----- We don't always generate minimal automata:+-- We don't always generate a minimal automata: -- -- >>> putPretty $ fromERE $ "a" /\ "b" -- 0 -> \_ -> 1 -- 1 -> \_ -> 1 -- black hole ----- Compare this to an @complement@ example+-- Compare this to a 'complement' example ----- Using 'fromTMEquiv', we can get minimal automaton, for the cost of higher+-- Using 'fromTMEquiv', we can get a minimal automaton, for the cost of higher -- complexity (slow!). -- -- >>> putPretty $ fromTMEquiv $ ("a" /\ "b" :: ERE.ERE Char)@@ -163,7 +170,8 @@     -> DFA c fromTMImpl mequiv re = DFA     { dfaTransition = transition-    , dfaAcceptable = IS.fromList+    , dfaInitial    = 0+    , dfaAcceptable = IntSet.fromList         [ i         | (re', i) <- Map.toList lookupMap         , nullable re'@@ -171,16 +179,16 @@     , dfaBlackholes = blackholes     }   where-    transition = IM.fromList+    transition = IntMap.fromList         [ (i, js)         | (re', pm) <- Map.toList tm         , let i  = fromMaybe 0 $ Map.lookup re' lookupMap         , let js = SF.normalise $ fmap (\re'' -> fromMaybe 0 $ Map.lookup re'' lookupMap) pm         ] -    blackholes = IS.fromList+    blackholes = IntSet.fromList         [ i-        | (i, sf) <- IM.toList transition+        | (i, sf) <- IntMap.toList transition         , sf == pure i         ] @@ -289,11 +297,11 @@ -- See <https://mathoverflow.net/questions/45149/can-regular-expressions-be-made-unambiguous> -- for the description of the algorithm used. ---toRE :: forall c. (Ord c, Enum c, Bounded c) => DFA c -> RE.RE c+toRE :: (Ord c, Enum c, Bounded c) => DFA c -> RE.RE c toRE = toKleene  -- | Convert 'DFA' to 'ERE.ERE'.-toERE :: forall c. (Ord c, Enum c, Bounded c) => DFA c -> ERE.ERE c+toERE :: (Ord c, Enum c, Bounded c) => DFA c -> ERE.ERE c toERE = toKleene  -- | Convert to any 'Kleene'.@@ -301,13 +309,13 @@ -- See 'toRE' for a specific example. -- toKleene :: forall k c. (Ord c, Enum c, Bounded c, FiniteKleene c k) => DFA c -> k-toKleene (DFA tr acc _) = unions-    [ re 0 j maxN-    | j <- IS.toList acc+toKleene (DFA tr ini acc _) = unions+    [ re ini j maxN+    | j <- IntSet.toList acc     ]   where-    maxN | IM.null tr = 1-         | otherwise = succ $ fst $ IM.findMax tr+    maxN | IntMap.null tr = 1+         | otherwise      = succ $ fst $ IntMap.findMax tr      {-     -- this is useful for debug@@ -321,8 +329,8 @@      re i j k = MT.memo re' (i, j, k)     re' (i, j, k)-        | k <= 0    = if i == j then eps \/ r else r-        | otherwise = re i j k' \/ (re i k' k' <> star (re k' k' k') <> re k' j k')+        | k <= 0    = if i == j then unions [eps, r] else r+        | otherwise = unions [re i j k', appends [re i k' k', star (re k' k' k'), re k' j k']]       where         r = maybe empty fromRSet $ Map.lookup (i, j) re0map         k' = k - 1@@ -330,7 +338,7 @@     re0map :: Map (Int, Int) (RSet c)     re0map = Map.fromListWith RSet.union         [ ((i, j), RSet.singletonRange (lo, hi))-        | (i, tr') <- IM.toList tr+        | (i, tr') <- IntMap.toList tr         , (lo, hi, j) <- toPieces tr'         ] @@ -350,7 +358,7 @@  -- | Run 'DFA' on the input. ----- Because we have analysed a language, in some cases we can determine an input+-- Because we have analysed a language, in some cases we can determine a result -- without traversing all of the input. -- That's not the cases with 'RE.RE' 'match'. --@@ -367,13 +375,21 @@ -- prop> all (match (fromRE r)) $ take 10 $ RE.generate (curry QC.choose) 42 (r :: RE.RE Char) -- instance Ord c => Match c (DFA c) where-    match (DFA tr acc bh) = go (0 :: Int) where-        go s _ | IS.member s bh = IS.member s acc-        go s []                 = IS.member s acc-        go s (c : cs)           = case IM.lookup s tr of+    match (DFA tr i acc bh) = go i where+        go !s _ | IntSet.member s bh = IntSet.member s acc+        go !s []                 = IntSet.member s acc+        go !s (c : cs)           = case IntMap.lookup s tr of             Nothing -> False             Just sf -> go (sf SF.! c) cs +    match8 (DFA tr i acc bh) = go i where+        go !s !_ | IntSet.member s bh = IntSet.member s acc+        go !s bs = case BS.uncons bs of+            Nothing      -> IntSet.member s acc+            Just (c, cs) -> case IntMap.lookup s tr of+                Nothing -> False+                Just sf -> go (sf SF.! c) cs+ -- | Complement DFA. -- -- Complement of 'DFA' is way easier than of 'RE.RE': complement accept states.@@ -398,19 +414,132 @@ -- [False,False,False,True,True,True] -- instance Complement c (DFA c) where-    complement (DFA tr acc err) = DFA tr acc' err where-        acc' = IS.difference (IM.keysSet tr) acc+    complement (DFA tr ini acc bh) = DFA tr ini acc' bh where+        acc' = IntSet.difference (IntMap.keysSet tr) acc +instance Ord c => Derivate c (DFA c) where+    nullable (DFA _tr ini acc _bh) = IntSet.member ini acc++    derivate c (DFA tr ini acc bh) = DFA tr ini' acc bh where+        ini' = case IntMap.lookup ini tr of+            Nothing -> ini -- in error case let's just stay in the same state.+            Just sf -> sf SF.! c+ -------------------------------------------------------------------------------+-- toDot+-------------------------------------------------------------------------------++-- | Get Graphviz dot-code of DFA.+--+-- >>> let dfa = fromRE $ RE.star "abc"+-- >>> putStr $ toDot dfa+-- digraph dfa {+-- rankdir=LR;+-- // states+-- "0" [shape=doublecircle];+-- "1" [shape=circle];+-- "2" [shape=circle];+-- // initial state+-- "" [shape=none];+-- "" -> "0";+-- // transitions+-- "0" -> "2"[label="a"]+-- "1" -> "0"[label="c"]+-- "2" -> "1"[label="b"]+-- }+--+toDot :: DFA Char -> String+toDot = toDot' show pure++-- | More flexible version of 'toDot'.+toDot' :: (Ord c, Enum c, Bounded c) => (Int -> String) -> (c -> String) -> DFA c -> String+toDot' showS showC (DFA tr ini acc bh)+    = showString "digraph dfa {\n"+    . showString "rankdir=LR;\n"+    . showString "// states\n"+    . showStates+    . showString "// initial state\n"+    . showInitial+    . showString "// transitions\n"+    . showTransitions+    . showString "}\n"+    $ ""+  where+    showStates  = foldr (.) id+        [ showState i+        | i <- IntMap.keys tr+        , IntSet.member i acc || IntSet.notMember i bh+        ]+    showState s = showS' s . shape where+        shape+            | IntSet.member s acc = showString " [shape=doublecircle];\n"+            | otherwise        = showString " [shape=circle];\n"++    showInitial+        = showString "\"\" [shape=none];\n"+        . showString "\"\" -> "+        . showS' ini+        . showString ";\n"++    showTransitions = foldr (.) id+        [ showS' i+        . showString " -> "+        . showS' j+        . showString "[label="+        . label+                . showString "]\n"+        | (i, sf) <- IntMap.toList tr+        , (lo, hi, j) <- toPieces sf+        , IntSet.member j acc || IntSet.notMember j bh+        , let label+                | lo == hi+                    = shows (showC lo)+                | lo == minBound && hi == maxBound+                    = shows ("-any" :: String)+                | otherwise+                    = shows (showC lo ++ "-" ++ showC hi)+        ]++    showS' = shows . showS++-------------------------------------------------------------------------------+-- Orphans+-------------------------------------------------------------------------------++-- | __WARNING__: The '/\' is inefficient, it actually computes the conjunction:+--+-- >>> putPretty $ asREChar $ "a" /\ "b"+-- ^[]$+--+-- >>> putPretty $ asREChar $ "a" /\ star "a"+-- ^a$+--+-- >>> putPretty $ asREChar $ star "aa" /\ star "aaa"+-- ^(a(aaaaaa)*aaaaa)?$+--+instance (Ord c, Enum c, Bounded c) => Lattice (RE.RE c) where+    r /\ r' = toRE $ fromERE $ ERE.fromRE r /\ ERE.fromRE r'+    r \/ r' = unions [r, r']++instance (Ord c, Enum c, Bounded c) => BoundedJoinSemiLattice (RE.RE c) where+    bottom = empty++instance (Ord c, Enum c, Bounded c) => BoundedMeetSemiLattice (RE.RE c) where+    top = RE.REStar (RE.REChars RSet.full)++instance (Ord c, Enum c, Bounded c) => Complement c (RE.RE c) where+    complement = toRE . complement . fromRE++------------------------------------------------------------------------------- -- Debug -------------------------------------------------------------------------------  instance Show c => Pretty (DFA c) where     pretty dfa = intercalate "\n"         [ show i ++ acc ++ " -> " ++ SF.showSF sf ++ bh-        | (i, sf) <- IM.toList (dfaTransition dfa)-        , let acc = if IS.member i (dfaAcceptable dfa) then "+" else ""-        , let bh = if IS.member i $ dfaBlackholes dfa then " -- black hole" else ""+        | (i, sf) <- IntMap.toList (dfaTransition dfa)+        , let acc = if IntSet.member i (dfaAcceptable dfa) then "+" else ""+        , let bh = if IntSet.member i $ dfaBlackholes dfa then " -- black hole" else ""         ]  -- $setup@@ -424,3 +553,5 @@ -- >>> newtype Smaller a = Smaller a deriving (Show) -- >>> let intLog2 = (`div` 10) -- >>> instance QC.Arbitrary a => QC.Arbitrary (Smaller a) where arbitrary = QC.scale intLog2 QC.arbitrary; shrink (Smaller a) = map Smaller (QC.shrink a)+--+-- >>> let asREChar :: RE.RE Char -> RE.RE Char; asREChar = id
src/Kleene/ERE.hs view
@@ -28,6 +28,8 @@     -- * Derivative     nullable,     derivate,+    -- * Conversion+    fromRE,     -- * Transition map     transitionMap,     leadingChars,@@ -38,13 +40,12 @@     isEverything,     ) where +import Data.Semigroup (Semigroup (..)) import Prelude () import Prelude.Compat  import Algebra.Lattice-       (BoundedJoinSemiLattice (..), BoundedLattice,-       BoundedMeetSemiLattice (..), JoinSemiLattice (..), Lattice,-       MeetSemiLattice (..))+       (BoundedJoinSemiLattice (..), BoundedMeetSemiLattice (..), Lattice (..)) import Control.Applicative (liftA2) import Data.Foldable       (toList) import Data.List           (foldl')@@ -62,6 +63,7 @@ import qualified Kleene.Classes            as C import qualified Kleene.Internal.Partition as P import           Kleene.Internal.Pretty+import qualified Kleene.Internal.RE        as RE  -- | Extended regular expression --@@ -85,6 +87,18 @@   deriving (Eq, Ord, Show)  -------------------------------------------------------------------------------+-- fromRE+-------------------------------------------------------------------------------++-- | Convert from ordinary regular expression, 'RE.RE'.+--+fromRE :: Ord c => RE.RE c -> ERE c+fromRE (RE.REChars rs)   = EREChars rs+fromRE (RE.REAppend rs)  = EREAppend (map fromRE rs)+fromRE (RE.REUnion r rs) = EREUnion r (Set.map fromRE rs)+fromRE (RE.REStar r)     = EREStar (fromRE r)++------------------------------------------------------------------------------- -- Smart constructor ------------------------------------------------------------------------------- @@ -182,8 +196,8 @@ -- prop> empty \/ asEREChar r === r -- prop> asEREChar r \/ empty === r ----- prop> everything \/ asREChar r === everything--- prop> asREChar r \/ everything === everything+-- prop> everything \/ asEREChar r === everything+-- prop> asEREChar r \/ everything === everything -- unions :: (Ord c, Enum c) => [ERE c] -> ERE c unions = uncurry mk . foldMap f where@@ -209,8 +223,8 @@ -- prop> empty /\ asEREChar r === empty -- prop> asEREChar r /\ empty === empty ----- prop> everything /\ asREChar r === r--- prop> asREChar r /\ everything === r+-- prop> everything /\ asEREChar r === r+-- prop> asEREChar r /\ everything === r -- intersections :: (Ord c, Enum c) => [ERE c] -> ERE c intersections = complement . unions . map complement@@ -232,7 +246,7 @@ -- prop> star empty   === asEREChar eps -- prop> star anyChar === asEREChar everything ----- prop> star (asREChar r \/ eps) === star r+-- prop> star (asEREChar r \/ eps) === star r -- prop> star (char c \/ eps) === star (char (c :: Char)) -- prop> star (empty \/ eps) === eps --@@ -263,14 +277,16 @@ string [c] = EREChars (RSet.singleton c) string cs  = EREAppend $ map (EREChars . RSet.singleton) cs -instance (Ord c, Enum c, Bounded c) => C.Kleene c (ERE c) where+instance (Ord c, Enum c, Bounded c) => C.Kleene (ERE c) where     empty      = empty     eps        = eps-    char       = char     appends    = appends     unions     = unions     star       = star +instance (Ord c, Enum c, Bounded c) => C.CharKleene c (ERE c) where+    char       = char+ instance (Ord c, Enum c, Bounded c) => C.FiniteKleene c (ERE c) where     everything = everything     charRange  = charRange@@ -508,21 +524,16 @@     mappend = (<>)     mconcat = appends -instance (Ord c, Enum c) => JoinSemiLattice (ERE c) where+instance (Ord c, Enum c) => Lattice (ERE c) where     r \/ r' = unions [r, r']+    r /\ r' = intersections [r, r']  instance (Ord c, Enum c) => BoundedJoinSemiLattice (ERE c) where     bottom = empty -instance (Ord c, Enum c) => MeetSemiLattice (ERE c) where-    r /\ r' = intersections [r, r']- instance (Ord c, Enum c) => BoundedMeetSemiLattice (ERE c) where     top = everything -instance  (Ord c, Enum c) => Lattice (ERE c)-instance  (Ord c, Enum c) => BoundedLattice (ERE c)- instance c ~ Char => IsString (ERE c) where     fromString = string @@ -592,6 +603,17 @@         parens :: Bool -> ShowS -> ShowS         parens True  s = showString "(" . s . showChar ')'         parens False s = s++-------------------------------------------------------------------------------+-- Latin1+-------------------------------------------------------------------------------++instance C.ToLatin1 ERE where+    toLatin1 (EREChars rs)    = C.fromRSet (C.toLatin1 rs)+    toLatin1 (EREAppend xs)   = appends (map C.toLatin1 xs)+    toLatin1 (EREUnion rs xs) = C.fromRSet (C.toLatin1 rs) \/ unions (map C.toLatin1 (Set.toList  xs))+    toLatin1 (EREStar r)      = star (C.toLatin1 r)+    toLatin1 (ERENot r)       = complement (C.toLatin1 r)  ------------------------------------------------------------------------------- -- Doctest
src/Kleene/Equiv.hs view
@@ -10,12 +10,12 @@ import Prelude () import Prelude.Compat -import Algebra.Lattice-       (BoundedJoinSemiLattice (..), JoinSemiLattice (..), joinLeq)+import Algebra.Lattice    (BoundedJoinSemiLattice (..), BoundedMeetSemiLattice (..), Lattice (..), joinLeq) import Algebra.PartialOrd (PartialOrd (..))+import Data.Semigroup     (Semigroup (..))  import Kleene.Classes-import           Kleene.Internal.Pretty+import Kleene.Internal.Pretty  -- | Regular-expressions for which '==' is 'equivalent'. --@@ -41,16 +41,17 @@ -- (False,False) -- newtype Equiv r c = Equiv (r c)-  deriving (Show, Semigroup, Monoid, BoundedJoinSemiLattice, JoinSemiLattice, Pretty)+  deriving (Show, Semigroup, Monoid, BoundedJoinSemiLattice, BoundedMeetSemiLattice, Lattice, Pretty)  instance Equivalent c (r c) => Eq (Equiv r c) where     (==) = equivalent  -- | \(a \preceq b := a \lor b = b \)-instance (JoinSemiLattice (r c), Equivalent c (r c)) => PartialOrd (Equiv r c) where+instance (Lattice (r c), Equivalent c (r c)) => PartialOrd (Equiv r c) where     leq = joinLeq -deriving instance Kleene     c (r c) => Kleene     c (Equiv r c)+deriving instance Kleene       (r c) => Kleene       (Equiv r c)+deriving instance CharKleene c (r c) => CharKleene c (Equiv r c) deriving instance Derivate   c (r c) => Derivate   c (Equiv r c) deriving instance Match      c (r c) => Match      c (Equiv r c) deriving instance Equivalent c (r c) => Equivalent c (Equiv r c)
src/Kleene/Functor.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE CPP   #-}-{-# LANGUAGE GADTs #-} {-# LANGUAGE Safe  #-} module Kleene.Functor (     K,@@ -25,249 +23,4 @@     toRA,     ) where -import Prelude ()-import Prelude.Compat--import Algebra.Lattice     ((\/))-import Control.Applicative (Alternative (..), liftA2)-import Data.Foldable       (toList)-import Data.RangeSet.Map   (RSet)-import Data.String         (IsString (..))--import qualified Data.RangeSet.Map      as RSet-import qualified Text.Regex.Applicative as R--import qualified Kleene.Classes         as C-import           Kleene.Internal.Pretty-import           Kleene.Internal.Sets-import qualified Kleene.RE              as RE---- | Star behaviour-data Greediness-    = Greedy    -- ^ 'many'-    | NonGreedy -- ^ 'few'-  deriving (Eq, Ord, Show, Enum, Bounded)---- | 'Applicative' 'Functor' regular expression.-data K c a where-    KEmpty  :: K c a-    KPure   :: a -> K c a-    KChar   :: (Ord c, Enum c) => RSet c -> K c c-    KAppend :: (a -> b -> r) -> K c a -> K c b -> K c r-    KUnion  :: K c a -> K c a -> K c a-    KStar   :: Greediness -> K c a -> K c [a]--    -- optimisations-    KMap    :: (a -> b) -> K c a -> K c b -- could use Pure and Append-    KString :: Eq c => [c] -> K c [c]     -- could use Char and Append--instance (c ~ Char, IsString a) => IsString (K c a) where-    fromString s = KMap fromString (KString s)--instance Functor (K c) where-    fmap _ KEmpty          = KEmpty-    fmap f (KPure x)       = KPure (f x)-    fmap f (KMap g k)      = KMap (f . g) k-    fmap f (KAppend g a b) = KAppend (\x y -> f (g x y)) a b-    fmap f k                    = KMap f k--instance Applicative (K c) where-    pure = KPure--    KEmpty <*> _ = KEmpty-    _ <*> KEmpty = KEmpty--    KPure f <*> k = fmap f k-    k <*> KPure x = fmap ($ x) k--    f <*> x = KAppend ($) f x--#if MIN_VERSION_base(4,10,0)-    liftA2 = KAppend-#endif--instance Alternative (K c) where-    empty = KEmpty--    KEmpty <|> k = k-    k <|> KEmpty = k-    KChar a <|> KChar b = KChar (RSet.union a b)--    a <|> b = KUnion a b--    many KEmpty      = KPure []-    many (KStar _ k) = KMap pure (KStar Greedy k)-    many k           = KStar Greedy k--    some KEmpty      = KEmpty-    some (KStar _ k) = KMap pure (KStar Greedy k)-    some k           = liftA2 (:) k (KStar Greedy k)---- | 'few', not 'many'.------ Let's define two similar regexps------ >>> let re1 = liftA2 (,) (few  $ char 'a') (many $ char 'a')--- >>> let re2 = liftA2 (,) (many $ char 'a') (few  $ char 'a')------ Their 'RE' behaviour is the same:------ >>> C.equivalent (toRE re1) (toRE re2)--- True------ >>> map (C.match $ toRE re1) ["aaa","bbb"]--- [True,False]------ However, the 'RA' behaviour is different!------ >>> R.match (toRA re1) "aaaa"--- Just ("","aaaa")------ >>> R.match (toRA re2) "aaaa"--- Just ("aaaa","")----few :: K c a -> K c [a]-few KEmpty      = KPure []-few (KStar _ k) = KMap pure (KStar NonGreedy k)-few k           = KStar NonGreedy k------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | >>> putPretty anyChar--- ^[^]$-anyChar :: (Ord c, Enum c, Bounded c) => K c c-anyChar = KChar RSet.full---- | >>> putPretty $ oneof ("foobar" :: [Char])--- ^[a-bfor]$-oneof :: (Ord c, Enum c, Foldable f) => f c -> K c c-oneof = KChar . RSet.fromList . toList---- | >>> putPretty $ char 'x'--- ^x$-char :: (Ord c, Enum c) => c -> K c c-char = KChar . RSet.singleton---- | >>> putPretty $ charRange 'a' 'z'--- ^[a-z]$-charRange :: (Enum c, Ord c) => c -> c -> K c c-charRange a b = KChar (RSet.singletonRange (a, b))---- | >>> putPretty dot--- ^.$-dot :: K Char Char-dot = KChar dotRSet---- | >>> putPretty everything--- ^[^]*$-everything :: (Ord c, Enum c, Bounded c) => K c [c]-everything = many anyChar---- | >>> putPretty everything1--- ^[^][^]*$-everything1 :: (Ord c, Enum c, Bounded c) => K c [c]-everything1 = some anyChar---- | Matches nothing?-isEmpty :: (Ord c, Enum c, Bounded c) => K c a -> Bool-isEmpty k = C.equivalent (toRE k) C.empty---- | Matches whole input?-isEverything :: (Ord c, Enum c, Bounded c) => K c a -> Bool-isEverything k = C.equivalent (toRE k) C.everything------------------------------------------------------------------------------------ Matching------------------------------------------------------------------------------------ | Match using @regex-applicative@-match :: K c a -> [c] -> Maybe a-match = R.match . toRA------------------------------------------------------------------------------------ RE------------------------------------------------------------------------------------ | Convert to 'RE'.------ >>> putPretty (toRE $ many "foo" :: RE.RE Char)--- ^(foo)*$----toRE :: (Ord c, Enum c, Bounded c) => K c a -> RE.RE c-toRE = toKleene---- | Convert to any 'Kleene'-toKleene :: C.FiniteKleene c k => K c a -> k-toKleene (KMap _ a)      = toKleene a-toKleene (KUnion a b)    = toKleene a \/ toKleene b-toKleene (KAppend _ a b) = toKleene a <> toKleene b-toKleene (KStar _ a)     = C.star (toKleene a)-toKleene (KString s)     = C.appends (map C.char s)-toKleene KEmpty          = C.empty-toKleene (KPure _)       = C.eps-toKleene (KChar cs)      = C.fromRSet cs---- | Convert from 'RE'.------ /Note:/ all 'RE.REStar's are converted to 'Greedy' ones,--- it doesn't matter, as we don't capture anything.------ >>> match (fromRE "foobar") "foobar"--- Just "foobar"------ >>> match (fromRE $ C.star "a" <> C.star "a") "aaaa"--- Just "aaaa"----fromRE :: (Ord c, Enum c) => RE.RE c -> K c [c]-fromRE (RE.REChars cs)    = pure <$> KChar cs-fromRE (RE.REAppend rs)   = concat <$> traverse fromRE rs-fromRE (RE.REUnion cs rs) = foldr (KUnion . fromRE) (pure <$> KChar cs) (toList rs)-fromRE (RE.REStar r)      = concat <$> KStar Greedy (fromRE r)------------------------------------------------------------------------------------ regex-applicative------------------------------------------------------------------------------------ | Convert 'K' to 'R.RE' from @regex-applicative@.------ >>> R.match (toRA ("xx" *> everything <* "zz" :: K Char String)) "xxyyyzz"--- Just "yyy"------ See also 'match'.----toRA :: K c a -> R.RE c a-toRA KEmpty              = empty-toRA (KPure x)           = pure x-toRA (KChar cs)          = R.psym (\c -> RSet.member c cs)-toRA (KAppend f a b)     = liftA2 f (toRA a) (toRA b)-toRA (KUnion a b)        = toRA a <|> toRA b-toRA (KStar Greedy a)    = many (toRA a)-toRA (KStar NonGreedy a) = R.few (toRA a)-toRA (KMap f a)          = fmap f (toRA a)-toRA (KString s)         = R.string s------------------------------------------------------------------------------------ JavaScript------------------------------------------------------------------------------------ | Convert to non-matching JavaScript string which can be used--- as an argument to @new RegExp@------ >>> putPretty ("foobar" :: K Char String)--- ^foobar$------ >>> putPretty $ many ("foobar" :: K Char String)--- ^(foobar)*$----instance c ~ Char => Pretty (K c a) where-    pretty = pretty . toRE------------------------------------------------------------------------------------ Doctest------------------------------------------------------------------------------------ $setup------ >>> :set -XOverloadedStrings+import Kleene.Internal.Functor
+ src/Kleene/Functor/NonEmpty.hs view
@@ -0,0 +1,287 @@+{-# LANGUAGE CPP   #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE Safe  #-}+module Kleene.Functor.NonEmpty (+    K1,+    Greediness (..),+    -- * Constructors+    some1,+    anyChar,+    oneof,+    char,+    charRange,+    dot,+    everything1,+    string,+    -- * Queries+    isEmpty,+    isEverything,+    -- * Matching+    match,+    -- * Conversions+    toRE,+    toKleene,+    toRA,+    nullableProof,+    ) where++import Prelude ()+import Prelude.Compat++import Control.Applicative (Alternative (..), liftA2)+import Data.Foldable       (toList)+import Data.Functor.Alt    ((<!>))+import Data.Functor.Apply  (Apply (..))+import Data.List.NonEmpty  (NonEmpty (..))+import Data.RangeSet.Map   (RSet)++import qualified Data.Functor.Alt       as Alt+import qualified Data.List.NonEmpty     as NE+import qualified Data.RangeSet.Map      as RSet+import qualified Text.Regex.Applicative as R++import qualified Kleene.Classes          as C+import           Kleene.Internal.Functor (Greediness (..), K (..))+import           Kleene.Internal.Pretty+import           Kleene.Internal.Sets+import qualified Kleene.RE               as RE++-- | 'Applicative' 'Functor' regular expression.+data K1 c a where+    K1Empty  :: K1 c a+    K1Char   :: (Ord c, Enum c) => RSet c -> K1 c c+    K1Append :: (a -> b -> r) -> K1 c a -> K1 c b -> K1 c r+    K1Union  :: K1 c a -> K1 c a -> K1 c a+    KPlus    :: Greediness -> K1 c a -> K1 c (NonEmpty a)++    -- optimisations+    K1Map    :: (a -> b) -> K1 c a -> K1 c b -- could use Pure and Append+    K1String :: Eq c => NonEmpty c -> K1 c (NonEmpty c)     -- could use Char and Append++instance Functor (K1 c) where+    fmap _ K1Empty          = K1Empty+    fmap f (K1Map g k)      = K1Map (f . g) k+    fmap f (K1Append g a b) = K1Append (\x y -> f (g x y)) a b+    fmap f k                = K1Map f k++instance Apply (K1 c) where+    K1Empty <.> _ = K1Empty+    _ <.> K1Empty = K1Empty++    f <.> x = K1Append ($) f x++    liftF2 = K1Append++instance Alt.Alt (K1 c) where+    K1Empty <!> k = k+    k <!> K1Empty = k+    K1Char a <!> K1Char b = K1Char (RSet.union a b)++    a <!> b = K1Union a b++--+some1 :: K1 c a -> K1 c (NonEmpty a)+some1 K1Empty     = K1Empty+some1 (KPlus _ k) = K1Map pure (KPlus Greedy k)+some1 k           = KPlus Greedy k++-- | 'few1', not 'some1'.+--+-- Let's define two similar regexps+--+-- >>> let re1 = liftF2 (,) (few1 $ char 'a')  (some1 $ char 'a')+-- >>> let re2 = liftF2 (,) (some1 $ char 'a') (few1  $ char 'a')+--+-- Their 'RE' behaviour is the same:+--+-- >>> C.equivalent (toRE re1) (toRE re2)+-- True+--+-- >>> map (C.match $ toRE re1) ["aaa","bbb"]+-- [True,False]+--+-- However, the 'RA' behaviour is different!+--+-- >>> R.match (toRA re1) "aaaaa"+-- Just ('a' :| "",'a' :| "aaa")+--+-- >>> R.match (toRA re2) "aaaaa"+-- Just ('a' :| "aaa",'a' :| "")+--+few1 :: K1 c a -> K1 c (NonEmpty a)+few1 K1Empty     = K1Empty+few1 (KPlus _ k) = K1Map pure (KPlus NonGreedy k)+few1 k           = KPlus NonGreedy k++-------------------------------------------------------------------------------+--+-------------------------------------------------------------------------------++-- | >>> putPretty anyChar+-- ^[^]$+anyChar :: (Ord c, Enum c, Bounded c) => K1 c c+anyChar = K1Char RSet.full++-- | >>> putPretty $ oneof ("foobar" :: [Char])+-- ^[a-bfor]$+oneof :: (Ord c, Enum c, Foldable f) => f c -> K1 c c+oneof = K1Char . RSet.fromList . toList++-- | >>> putPretty $ char 'x'+-- ^x$+char :: (Ord c, Enum c) => c -> K1 c c+char = K1Char . RSet.singleton++-- | >>> putPretty $ charRange 'a' 'z'+-- ^[a-z]$+charRange :: (Enum c, Ord c) => c -> c -> K1 c c+charRange a b = K1Char (RSet.singletonRange (a, b))++-- | >>> putPretty dot+-- ^.$+dot :: K1 Char Char+dot = K1Char dotRSet++-- | >>> putPretty everything1+-- ^[^][^]*$+everything1 :: (Ord c, Enum c, Bounded c) => K1 c (NonEmpty c)+everything1 = some1 anyChar++-- | Matches nothing?+isEmpty :: (Ord c, Enum c, Bounded c) => K1 c a -> Bool+isEmpty k = C.equivalent (toRE k) C.empty++-- | Matches whole input?+isEverything :: (Ord c, Enum c, Bounded c) => K1 c a -> Bool+isEverything k = C.equivalent (toRE k) C.everything++string :: String -> K1 Char (NonEmpty Char)+string []       = error "panic! K1.string []"+string (x : xs) = K1String (x :| xs)++-------------------------------------------------------------------------------+-- Matching+-------------------------------------------------------------------------------++-- | Match using @regex-applicative@+match :: K1 c a -> [c] -> Maybe a+match = R.match . toRA++-------------------------------------------------------------------------------+-- RE+-------------------------------------------------------------------------------++-- | Convert to 'RE'.+--+-- >>> putPretty (toRE $ some1 (string "foo") :: RE.RE Char)+-- ^foo(foo)*$+--+toRE :: (Ord c, Enum c, Bounded c) => K1 c a -> RE.RE c+toRE = toKleene++-- | Convert to any 'Kleene'+toKleene :: C.FiniteKleene c k => K1 c a -> k+toKleene (K1Map _ a)      = toKleene a+toKleene (K1Union a b)    = C.unions [toKleene a, toKleene b]+toKleene (K1Append _ a b) = C.appends [toKleene a, toKleene b]+toKleene (KPlus _ a)      = let k = toKleene a in C.appends [k, C.star k]+toKleene (K1String s)     = C.appends (map C.char $ NE.toList s)+toKleene K1Empty          = C.empty+toKleene (K1Char cs)      = C.fromRSet cs++-------------------------------------------------------------------------------+-- regex-applicative+-------------------------------------------------------------------------------++-- | Convert 'K' to 'R.RE' from @regex-applicative@.+--+-- >>> R.match (toRA (string "xx" .> everything1 <. string "zz" :: K1 Char (NonEmpty Char))) "xxyyzyyzz"+-- Just ('y' :| "yzyy")+--+-- See also 'match'.+--+toRA :: K1 c a -> R.RE c a+toRA K1Empty              = empty+toRA (K1Char cs)          = R.psym (\c -> RSet.member c cs)+toRA (K1Append f a b)     = liftA2 f (toRA a) (toRA b)+toRA (K1Union a b)        = toRA a <|> toRA b+toRA (KPlus Greedy a)     = (:|) <$> toRA a <*> many (toRA a)+toRA (KPlus NonGreedy a)  = (:|) <$> toRA a <*> R.few (toRA a)+toRA (K1Map f a)          = fmap f (toRA a)+toRA (K1String (x :| xs)) = (:|) <$> R.sym x <*> R.string xs++-------------------------------------------------------------------------------+-- nullableProof+-------------------------------------------------------------------------------++-- |+-- >>> putPretty $ nullableProof (pure True)+-- Right 1 , ^[]$+--+-- >>> putPretty $ nullableProof (many "xyz" :: K Char [String])+-- Right [] , ^xyz(xyz)*$+--+-- >>> putPretty $ nullableProof (many $ toList <$> optional "x" <|> many "yz" :: K Char [[String]])+-- Right [] , ^(x|yz(yz)*)(x|yz(yz)*)*$+--+nullableProof :: K c a -> Either (K1 c a) (a, K1 c a)+nullableProof KEmpty    = Left K1Empty+nullableProof (KPure x) = Right (x, K1Empty)+nullableProof (KChar c) = Left (K1Char c)++nullableProof (KAppend f a b) = case (nullableProof a, nullableProof b) of+    (Left x, Left y)               -> Left (K1Append f x y)+    (Left x, Right (y', y))        -> Left ((`f` y') <$> x <!> K1Append f x y)+    (Right (x', x), Left y)        -> Left (K1Append f x y <!> f x' <$> y)+    (Right (x', x), Right (y', y)) -> Right+        (f x' y'+        , K1Append f x y+        <!> flip f y' <$> x+        <!> f x' <$> y+        )++nullableProof (KUnion a b) = case (nullableProof a, nullableProof b) of+    (Left x', Left _)              -> Left x'+    (Right (x, x'), Left y')       -> Right (x, x' <!> y')+    (Left x', Right (y, y'))       -> Right (y, x' <!> y')+    (Right (x, x'), Right (_, y')) -> Right (x, x' <!> y')++nullableProof (KStar g a) = case nullableProof a of+    Left x       -> Right ([], NE.toList <$> star1 x)+    Right (_, x) -> Right ([], NE.toList <$> star1 x) -- note, we don't left recurse+  where+    star1 = case g of+        Greedy    -> some1+        NonGreedy -> few1++nullableProof (KMap f a) = case nullableProof a of+    Right (x, x') -> Right (f x, fmap f x')+    Left x'       -> Left (fmap f x')++nullableProof (KString [])       = Right ([], K1Empty)+nullableProof (KString (c : cs)) = Left (NE.toList <$> K1String (c :| cs))++-------------------------------------------------------------------------------+-- JavaScript+-------------------------------------------------------------------------------++-- | Convert to non-matching JavaScript string which can be used+-- as an argument to @new RegExp@+--+-- >>> putPretty ("foobar" :: K Char String)+-- ^foobar$+--+-- >>> putPretty $ many ("foobar" :: K Char String)+-- ^(foobar)*$+--+instance c ~ Char => Pretty (K1 c a) where+    pretty = pretty . toRE++-------------------------------------------------------------------------------+-- Doctest+-------------------------------------------------------------------------------++-- $setup+--+-- >>> :set -XOverloadedStrings+-- >>> import Control.Applicative (optional)
+ src/Kleene/Internal/Functor.hs view
@@ -0,0 +1,282 @@+{-# LANGUAGE CPP   #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE Safe  #-}+module Kleene.Internal.Functor (+    K (..),+    Greediness (..),+    -- * Constructors+    few,+    anyChar,+    oneof,+    char,+    charRange,+    dot,+    everything,+    everything1,+    -- * Queries+    isEmpty,+    isEverything,+    -- * Matching+    match,+    -- * Conversions+    toRE,+    toKleene,+    fromRE,+    toRA,+    ) where++import Prelude ()+import Prelude.Compat++import Control.Applicative (Alternative (..), liftA2)+import Data.Foldable       (toList)+import Data.Functor.Apply  (Apply (..))+import Data.RangeSet.Map   (RSet)+import Data.String         (IsString (..))++import qualified Data.RangeSet.Map      as RSet+import qualified Text.Regex.Applicative as R+import qualified Data.Functor.Alt       as Alt++import qualified Kleene.Classes         as C+import           Kleene.Internal.Pretty+import           Kleene.Internal.Sets+import qualified Kleene.RE              as RE++-- | Star behaviour+data Greediness+    = Greedy    -- ^ 'many'+    | NonGreedy -- ^ 'few'+  deriving (Eq, Ord, Show, Enum, Bounded)++-- | 'Applicative' 'Functor' regular expression.+data K c a where+    KEmpty  :: K c a+    KPure   :: a -> K c a+    KChar   :: (Ord c, Enum c) => RSet c -> K c c+    KAppend :: (a -> b -> r) -> K c a -> K c b -> K c r+    KUnion  :: K c a -> K c a -> K c a+    KStar   :: Greediness -> K c a -> K c [a]++    -- optimisations+    KMap    :: (a -> b) -> K c a -> K c b -- could use Pure and Append+    KString :: Eq c => [c] -> K c [c]     -- could use Char and Append++instance (c ~ Char, IsString a) => IsString (K c a) where+    fromString s = KMap fromString (KString s)++instance Functor (K c) where+    fmap _ KEmpty          = KEmpty+    fmap f (KPure x)       = KPure (f x)+    fmap f (KMap g k)      = KMap (f . g) k+    fmap f (KAppend g a b) = KAppend (\x y -> f (g x y)) a b+    fmap f k                    = KMap f k++instance Apply (K c) where+    KEmpty <.> _ = KEmpty+    _ <.> KEmpty = KEmpty++    KPure f <.> k = fmap f k+    k <.> KPure x = fmap ($ x) k++    f <.> x = KAppend ($) f x++    liftF2 = KAppend++instance Applicative (K c) where+    pure  = KPure+    (<*>) = (<.>)++#if MIN_VERSION_base(4,10,0)+    liftA2 = liftF2+#endif++instance Alt.Alt (K c) where+    KEmpty <!> k = k+    k <!> KEmpty = k+    KChar a <!> KChar b = KChar (RSet.union a b)++    a <!> b = KUnion a b++    many KEmpty      = KPure []+    many (KStar _ k) = KMap pure (KStar Greedy k)+    many k           = KStar Greedy k++    some KEmpty      = KEmpty+    some (KStar _ k) = KMap pure (KStar Greedy k)+    some k           = liftA2 (:) k (KStar Greedy k)++instance Alternative (K c) where+    empty = KEmpty+    (<|>) = (Alt.<!>)+    some  = Alt.some+    many  = Alt.many++-- | 'few', not 'many'.+--+-- Let's define two similar regexps+--+-- >>> let re1 = liftA2 (,) (few  $ char 'a') (many $ char 'a')+-- >>> let re2 = liftA2 (,) (many $ char 'a') (few  $ char 'a')+--+-- Their 'RE' behaviour is the same:+--+-- >>> C.equivalent (toRE re1) (toRE re2)+-- True+--+-- >>> map (C.match $ toRE re1) ["aaa","bbb"]+-- [True,False]+--+-- However, the 'RA' behaviour is different!+--+-- >>> R.match (toRA re1) "aaaa"+-- Just ("","aaaa")+--+-- >>> R.match (toRA re2) "aaaa"+-- Just ("aaaa","")+--+few :: K c a -> K c [a]+few KEmpty      = KPure []+few (KStar _ k) = KMap pure (KStar NonGreedy k)+few k           = KStar NonGreedy k++-------------------------------------------------------------------------------+--+-------------------------------------------------------------------------------++-- | >>> putPretty anyChar+-- ^[^]$+anyChar :: (Ord c, Enum c, Bounded c) => K c c+anyChar = KChar RSet.full++-- | >>> putPretty $ oneof ("foobar" :: [Char])+-- ^[a-bfor]$+oneof :: (Ord c, Enum c, Foldable f) => f c -> K c c+oneof = KChar . RSet.fromList . toList++-- | >>> putPretty $ char 'x'+-- ^x$+char :: (Ord c, Enum c) => c -> K c c+char = KChar . RSet.singleton++-- | >>> putPretty $ charRange 'a' 'z'+-- ^[a-z]$+charRange :: (Enum c, Ord c) => c -> c -> K c c+charRange a b = KChar (RSet.singletonRange (a, b))++-- | >>> putPretty dot+-- ^.$+dot :: K Char Char+dot = KChar dotRSet++-- | >>> putPretty everything+-- ^[^]*$+everything :: (Ord c, Enum c, Bounded c) => K c [c]+everything = many anyChar++-- | >>> putPretty everything1+-- ^[^][^]*$+everything1 :: (Ord c, Enum c, Bounded c) => K c [c]+everything1 = some anyChar++-- | Matches nothing?+isEmpty :: (Ord c, Enum c, Bounded c) => K c a -> Bool+isEmpty k = C.equivalent (toRE k) C.empty++-- | Matches whole input?+isEverything :: (Ord c, Enum c, Bounded c) => K c a -> Bool+isEverything k = C.equivalent (toRE k) C.everything++-------------------------------------------------------------------------------+-- Matching+-------------------------------------------------------------------------------++-- | Match using @regex-applicative@+match :: K c a -> [c] -> Maybe a+match = R.match . toRA++-------------------------------------------------------------------------------+-- RE+-------------------------------------------------------------------------------++-- | Convert to 'RE'.+--+-- >>> putPretty (toRE $ many "foo" :: RE.RE Char)+-- ^(foo)*$+--+toRE :: (Ord c, Enum c, Bounded c) => K c a -> RE.RE c+toRE = toKleene++-- | Convert to any 'Kleene'+toKleene :: C.FiniteKleene c k => K c a -> k+toKleene (KMap _ a)      = toKleene a+toKleene (KUnion a b)    = C.unions [toKleene a, toKleene b]+toKleene (KAppend _ a b) = C.appends [toKleene a, toKleene b]+toKleene (KStar _ a)     = C.star (toKleene a)+toKleene (KString s)     = C.appends (map C.char s)+toKleene KEmpty          = C.empty+toKleene (KPure _)       = C.eps+toKleene (KChar cs)      = C.fromRSet cs++-- | Convert from 'RE'.+--+-- /Note:/ all 'RE.REStar's are converted to 'Greedy' ones,+-- it doesn't matter, as we don't capture anything.+--+-- >>> match (fromRE "foobar") "foobar"+-- Just "foobar"+--+-- >>> match (fromRE $ C.star "a" <> C.star "a") "aaaa"+-- Just "aaaa"+--+fromRE :: (Ord c, Enum c) => RE.RE c -> K c [c]+fromRE (RE.REChars cs)    = pure <$> KChar cs+fromRE (RE.REAppend rs)   = concat <$> traverse fromRE rs+fromRE (RE.REUnion cs rs) = foldr (KUnion . fromRE) (pure <$> KChar cs) (toList rs)+fromRE (RE.REStar r)      = concat <$> KStar Greedy (fromRE r)++-------------------------------------------------------------------------------+-- regex-applicative+-------------------------------------------------------------------------------++-- | Convert 'K' to 'R.RE' from @regex-applicative@.+--+-- >>> R.match (toRA ("xx" *> everything <* "zz" :: K Char String)) "xxyyyzz"+-- Just "yyy"+--+-- See also 'match'.+--+toRA :: K c a -> R.RE c a+toRA KEmpty              = empty+toRA (KPure x)           = pure x+toRA (KChar cs)          = R.psym (\c -> RSet.member c cs)+toRA (KAppend f a b)     = liftA2 f (toRA a) (toRA b)+toRA (KUnion a b)        = toRA a <|> toRA b+toRA (KStar Greedy a)    = many (toRA a)+toRA (KStar NonGreedy a) = R.few (toRA a)+toRA (KMap f a)          = fmap f (toRA a)+toRA (KString s)         = R.string s++-------------------------------------------------------------------------------+-- JavaScript+-------------------------------------------------------------------------------++-- | Convert to non-matching JavaScript string which can be used+-- as an argument to @new RegExp@+--+-- >>> putPretty ("foobar" :: K Char String)+-- ^foobar$+--+-- >>> putPretty $ many ("foobar" :: K Char String)+-- ^(foobar)*$+--+instance c ~ Char => Pretty (K c a) where+    pretty = pretty . toRE++-------------------------------------------------------------------------------+-- Doctest+-------------------------------------------------------------------------------++-- $setup+--+-- >>> :set -XOverloadedStrings
src/Kleene/Internal/Partition.hs view
@@ -3,14 +3,15 @@  import Prelude () import Prelude.Compat+import Data.Semigroup (Semigroup (..)) -import Data.Foldable             (toList)-import Data.List.NonEmpty.Compat (NonEmpty (..))-import Data.RangeSet.Map         (RSet)-import Data.Set                  (Set)+import Data.Foldable      (toList)+import Data.List.NonEmpty (NonEmpty (..))+import Data.RangeSet.Map  (RSet)+import Data.Set           (Set)  import qualified Data.Function.Step.Discrete.Closed as SF-import qualified Data.List.NonEmpty.Compat          as NE+import qualified Data.List.NonEmpty          as NE import qualified Data.RangeSet.Map                  as RSet import qualified Data.Set                           as Set @@ -125,7 +126,7 @@  -- | ----- prop> all (curry (<=)) $ intervals $ asPartitionChar p+-- prop> all (uncurry (<=)) $ intervals $ asPartitionChar p intervals :: (Enum a, Bounded a, Ord a) => Partition a -> NonEmpty (a, a) intervals (Partition xs) = go minBound (toList xs) where     go x []       = (x, maxBound) :| []
src/Kleene/Internal/Pretty.hs view
@@ -80,3 +80,17 @@  instance Pretty () where     prettyS _ = showChar '.'++instance Pretty a => Pretty (Maybe a) where+    prettyS Nothing  = showString "Nothing"+    prettyS (Just x) = prettyS x++instance (Pretty a, Pretty b) => Pretty (Either a b) where+    prettyS (Left x)  = showString "Left " . prettyS x+    prettyS (Right x) = showString "Right " . prettyS x++instance (Pretty a, Pretty b) => Pretty (a, b) where+    prettyS (x, y) = prettyS x . showString " , " . prettyS y++instance Show a => Pretty [a] where+    prettyS = showList
+ src/Kleene/Internal/RE.hs view
@@ -0,0 +1,676 @@+{-# LANGUAGE BangPatterns           #-}+{-# LANGUAGE FlexibleInstances      #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs                  #-}+{-# LANGUAGE Safe                   #-}+{-# LANGUAGE ScopedTypeVariables    #-}+module Kleene.Internal.RE (+    RE (..),+    -- * Construction+    --+    -- | Binary operators are+    --+    -- * '<>' for append+    -- * '\/' for union+    --+    empty,+    eps,+    char,+    charRange,+    anyChar,+    appends,+    unions,+    star,+    string,+    -- * Derivative+    nullable,+    derivate,+    -- * Transition map+    transitionMap,+    leadingChars,+    -- * Equivalence+    equivalent,+    -- * Generation+    generate,+    -- * Other+    isEmpty,+    nullableProof,+    ) where++import Prelude ()+import Prelude.Compat+import Data.Semigroup (Semigroup (..))++import Control.Applicative (liftA2)+import Data.Foldable       (toList)+import Data.List           (foldl')+import Data.Map            (Map)+import Data.RangeSet.Map   (RSet)+import Data.Set            (Set)+import Data.String         (IsString (..))++import qualified Data.Function.Step.Discrete.Closed as SF+import qualified Data.Map                           as Map+import qualified Data.RangeSet.Map                  as RSet+import qualified Data.Set                           as Set+import qualified Test.QuickCheck                    as QC+import qualified Test.QuickCheck.Gen                as QC (unGen)+import qualified Test.QuickCheck.Random             as QC (mkQCGen)++import qualified Kleene.Classes            as C+import qualified Kleene.Internal.Partition as P+import           Kleene.Internal.Pretty++-- | Regular expression+--+-- Constructors are exposed, but you should use+-- smart constructors in this module to construct 'RE'.+--+-- The 'Eq' and 'Ord' instances are structural.+-- The 'Kleene' etc constructors do "weak normalisation", so for values+-- constructed using those operations 'Eq' witnesses "weak equivalence".+-- See 'equivalent' for regular-expression equivalence.+--+-- Structure is exposed in "Kleene.RE" module but consider constructors as+-- half-internal.  There are soft-invariants, but violating them shouldn't+-- break anything in the package. (e.g. 'transitionMap' will eventually+-- terminate, but may create more redundant states if starting regexp is not+-- "weakly normalised").+--+data RE c+    = REChars (RSet c)               -- ^ Single character+    | REAppend [RE c]                -- ^ Concatenation+    | REUnion (RSet c) (Set (RE c))  -- ^ Union+    | REStar (RE c)                  -- ^ Kleene star+  deriving (Eq, Ord, Show)++-------------------------------------------------------------------------------+-- Smart constructor+-------------------------------------------------------------------------------++-- | Empty regex. Doesn't accept anything.+--+-- >>> putPretty (empty :: RE Char)+-- ^[]$+--+-- >>> putPretty (bottom :: RE Char)+-- ^[]$+--+-- prop> match (empty :: RE Char) (s :: String) === False+--+empty :: RE c+empty = REChars RSet.empty++-- | Everything.+--+-- >>> putPretty everything+-- ^[^]*$+--+-- prop> match (everything :: RE Char) (s :: String) === True+--+everything :: Bounded c => RE c+everything = REStar (REChars RSet.full)++-- | Empty string. /Note:/ different than 'empty'.+--+-- >>> putPretty eps+-- ^$+--+-- >>> putPretty (mempty :: RE Char)+-- ^$+--+-- prop> match (eps :: RE Char) s === null (s :: String)+--+eps :: RE c+eps = REAppend []++-- |+--+-- >>> putPretty (char 'x')+-- ^x$+--+char :: c -> RE c+char = REChars . RSet.singleton++-- |+--+-- >>> putPretty $ charRange 'a' 'z'+-- ^[a-z]$+--+charRange :: Ord c => c -> c -> RE c+charRange c c' = REChars $ RSet.singletonRange (c, c')++-- | Any character. /Note:/ different than dot!+--+-- >>> putPretty anyChar+-- ^[^]$+--+anyChar :: Bounded c => RE c+anyChar = REChars RSet.full++-- | Concatenate regular expressions.+--+-- prop> (asREChar r <> s) <> t === r <> (s <> t)+--+-- prop> asREChar r <> empty === empty+-- prop> empty <> asREChar r === empty+--+-- prop> asREChar r <> eps === r+-- prop> eps <> asREChar r === r+--+appends :: Eq c => [RE c] -> RE c+appends rs0+    | elem empty rs1 = empty+    | otherwise = case rs1 of+        [r] -> r+        rs  -> REAppend rs+  where+    -- flatten one level of REAppend+    rs1 = concatMap f rs0++    f (REAppend rs) = rs+    f r             = [r]++-- | Union of regular expressions.+--+-- prop> asREChar r \/ r === r+-- prop> asREChar r \/ s === s \/ r+-- prop> (asREChar r \/ s) \/ t === r \/ (s \/ t)+--+-- prop> empty \/ asREChar r === r+-- prop> asREChar r \/ empty === r+--+-- prop> everything \/ asREChar r === everything+-- prop> asREChar r \/ everything === everything+--+unions :: (Ord c, Enum c, Bounded c) => [RE c] -> RE c+unions = uncurry mk . foldMap f where+    mk cs rss+        | Set.null rss = REChars cs+        | Set.member everything rss = everything+        | RSet.null cs = case Set.toList rss of+            []  -> empty+            [r] -> r+            _   -> REUnion cs rss+        | otherwise    = REUnion cs rss++    f (REUnion cs rs) = (cs, rs)+    f (REChars cs)    = (cs, Set.empty)+    f r               = (mempty, Set.singleton r)++-- | Kleene star.+--+-- prop> star (star r) === star (asREChar r)+--+-- prop> star eps     === asREChar eps+-- prop> star empty   === asREChar eps+-- prop> star anyChar === asREChar everything+--+-- prop> star (r      \/ eps) === star (asREChar r)+-- prop> star (char c \/ eps) === star (asREChar (char c))+-- prop> star (empty  \/ eps) === asREChar eps+--+star :: Ord c => RE c -> RE c+star r = case r of+    REStar _                          -> r+    REAppend []                       -> eps+    REChars cs | RSet.null cs         -> eps+    REUnion cs rs | Set.member eps rs -> case Set.toList rs' of+        []                  -> star (REChars cs)+        [r'] | RSet.null cs -> star r'+        _                   -> REStar (REUnion cs rs')+      where+        rs' = Set.delete eps rs+    _                                 -> REStar r++-- | Literal string.+--+-- >>> putPretty ("foobar" :: RE Char)+-- ^foobar$+--+-- >>> putPretty ("(.)" :: RE Char)+-- ^\(\.\)$+--+string :: [c] -> RE c+string []  = eps+string [c] = REChars (RSet.singleton c)+string cs  = REAppend $ map (REChars . RSet.singleton) cs++instance (Ord c, Enum c, Bounded c) => C.Kleene (RE c) where+    empty      = empty+    eps        = eps+    appends    = appends+    unions     = unions+    star       = star++instance (Ord c, Enum c, Bounded c) => C.CharKleene c (RE c) where+    char       = char++instance (Ord c, Enum c, Bounded c) => C.FiniteKleene c (RE c) where+    everything = everything+    charRange  = charRange+    fromRSet   = REChars+    anyChar    = anyChar++-------------------------------------------------------------------------------+-- Pseudo lattice+-------------------------------------------------------------------------------++(\/) :: (Ord c, Enum c, Bounded c) => RE c -> RE c -> RE c+r \/ r' = unions [r, r']++-------------------------------------------------------------------------------+-- derivative+-------------------------------------------------------------------------------++-- | We say that a regular expression r is nullable if the language it defines+-- contains the empty string.+--+-- >>> nullable eps+-- True+--+-- >>> nullable (star "x")+-- True+--+-- >>> nullable "foo"+-- False+--+nullable :: RE c -> Bool+nullable (REChars _)      = False+nullable (REAppend rs)    = all nullable rs+nullable (REUnion _cs rs) = any nullable rs+nullable (REStar _)       = True++-- | Intuitively, the derivative of a language \(\mathcal{L} \subset \Sigma^\star\)+-- with respect to a symbol \(a \in \Sigma\) is the language that includes only+-- those suffixes of strings with a leading symbol \(a\) in \(\mathcal{L}\).+--+-- >>> putPretty $ derivate 'f' "foobar"+-- ^oobar$+--+-- >>> putPretty $ derivate 'x' $ "xyz" \/ "abc"+-- ^yz$+--+-- >>> putPretty $ derivate 'x' $ star "xyz"+-- ^yz(xyz)*$+--+derivate :: (Ord c, Enum c, Bounded c) => c -> RE c -> RE c+derivate c (REChars cs)     = derivateChars c cs+derivate c (REUnion cs rs)  = unions $ derivateChars c cs : [ derivate c r | r <- toList rs]+derivate c (REAppend rs)    = derivateAppend c rs+derivate c rs@(REStar r)    = derivate c r <> rs++derivateAppend :: (Ord c, Enum c, Bounded c) => c -> [RE c] -> RE c+derivateAppend _ []      = empty+derivateAppend c [r]     = derivate c r+derivateAppend c (r:rs)+    | nullable r         = unions [r' <> appends rs, rs']+    | otherwise          = r' <> appends rs+  where+    r'  = derivate c r+    rs' = derivateAppend c rs++derivateChars :: Ord c =>  c -> RSet c -> RE c+derivateChars c cs+    | c `RSet.member` cs      = eps+    | otherwise               = empty++instance (Ord c, Enum c, Bounded c) => C.Derivate c (RE c) where+    nullable = nullable+    derivate = derivate++instance (Ord c, Enum c, Bounded c) => C.Match c (RE c) where+    match r = nullable . foldl' (flip derivate) r++-------------------------------------------------------------------------------+-- Nullable with proof+-------------------------------------------------------------------------------++-- | Not only we can decide whether 'RE' is nullable, we can also+-- remove the empty string:+--+-- >>> putPretty $ nullableProof eps+-- ^[]$+--+-- >>> putPretty $ nullableProof $ star "x"+-- ^xx*$+--+-- >>> putPretty $ nullableProof "foo"+-- Nothing+--+-- 'nullableProof' is consistent with 'nullable':+--+-- prop> isJust (nullableProof r) === nullable (asREChar r)+--+-- The returned regular expression is not nullable:+--+-- prop> maybe True (not . nullable) $ nullableProof $ asREChar r+--+-- If we union with empty regex, we get a equivalent regular expression+-- we started with:+--+-- prop> maybe r (eps \/) (nullableProof r) `equivalent` (asREChar r)+--+nullableProof :: forall c. (Ord c, Enum c, Bounded c) => RE c -> Maybe (RE c)+nullableProof (REChars _)   = Nothing++nullableProof (REAppend []) = Just empty+nullableProof (REAppend xs)+    | Just ys <- traverse (\x -> (,) x <$> nullableProof x) xs = Just (go ys)+    | otherwise = Nothing+  where+    go :: [(RE c, RE c)] -> RE c+    go rs = unions $ map appends $ tail $ traverse (\(r,r') -> [r,r']) rs++nullableProof (REUnion cs rs)+    | any nullable rs = Just $ REUnion cs $ Set.map (\r -> maybe r id $ nullableProof r) rs+    | otherwise       = Nothing++nullableProof (REStar r)+    | Just r' <- nullableProof r = Just (r' <> REStar r')+    | otherwise                  = Just (r <> REStar r) ++-------------------------------------------------------------------------------+-- isEmpty+-------------------------------------------------------------------------------++-- | Whether 'RE' is (structurally) equal to 'empty'.+--+-- prop> isEmpty r === all (not . nullable) (Map.keys $ transitionMap $ asREChar r)+isEmpty :: RE c -> Bool+isEmpty (REChars rs) = RSet.null rs+isEmpty _            = False++-------------------------------------------------------------------------------+-- States+-------------------------------------------------------------------------------++-- | Transition map. Used to construct 'Kleene.DFA.DFA'.+--+-- >>> void $ Map.traverseWithKey (\k v -> putStrLn $ pretty k ++ " : " ++ SF.showSF (fmap pretty v)) $ transitionMap ("ab" :: RE Char)+-- ^[]$ : \_ -> "^[]$"+-- ^b$ : \x -> if+--     | x <= 'a'  -> "^[]$"+--     | x <= 'b'  -> "^$"+--     | otherwise -> "^[]$"+-- ^$ : \_ -> "^[]$"+-- ^ab$ : \x -> if+--     | x <= '`'  -> "^[]$"+--     | x <= 'a'  -> "^b$"+--     | otherwise -> "^[]$"+--+transitionMap+    :: forall c. (Ord c, Enum c, Bounded c)+    => RE c+    -> Map (RE c) (SF.SF c (RE c))+transitionMap re = go Map.empty [re] where+    go :: Map (RE c) (SF.SF c (RE c))+       -> [RE c]+       -> Map (RE c) (SF.SF c (RE c))+    go !acc [] = acc+    go acc (r : rs)+        | r `Map.member` acc = go acc rs+        | otherwise = go (Map.insert r pm acc) (SF.values pm ++ rs)+      where+        pm = P.toSF (\c -> derivate c r) (leadingChars r)++instance (Ord c, Enum c, Bounded c) => C.TransitionMap c (RE c) where+    transitionMap = transitionMap++-- | Leading character sets of regular expression.+--+-- >>> leadingChars "foo"+-- fromSeparators "ef"+--+-- >>> leadingChars (star "b" <> star "e")+-- fromSeparators "abde"+--+-- >>> leadingChars (charRange 'b' 'z')+-- fromSeparators "az"+--+leadingChars :: (Ord c, Enum c, Bounded c) => RE c -> P.Partition c+leadingChars (REChars cs)    = P.fromRSet cs+leadingChars (REUnion cs rs) = P.fromRSet cs <> foldMap leadingChars rs+leadingChars (REStar r)      = leadingChars r+leadingChars (REAppend rs)   = leadingCharsAppend rs++leadingCharsAppend :: (Ord c, Enum c, Bounded c) => [RE c] -> P.Partition c+leadingCharsAppend [] = P.whole+leadingCharsAppend (r : rs)+    | nullable r = leadingChars r <> leadingCharsAppend rs+    | otherwise  = leadingChars r++-------------------------------------------------------------------------------+-- Equivalence+-------------------------------------------------------------------------------++-- | Whether two regexps are equivalent.+--+-- @+-- 'equivalent' re1 re2 <=> forall s. 'match' re1 s === 'match' re1 s+-- @+--+-- >>> let re1 = star "a" <> "a"+-- >>> let re2 = "a" <> star "a"+--+-- These are different regular expressions, even we perform+-- some normalisation-on-construction:+--+-- >>> re1 == re2+-- False+--+-- They are however equivalent:+--+-- >>> equivalent re1 re2+-- True+--+-- The algorithm works by executing 'states' on "product" regexp,+-- and checking whether all resulting states are both accepting or rejecting.+--+-- @+-- re1 == re2 ==> 'equivalent' re1 re2+-- @+--+-- === More examples+--+-- >>> let example re1 re2 = putPretty re1 >> putPretty re2 >> return (equivalent re1 re2)+-- >>> example re1 re2+-- ^a*a$+-- ^aa*$+-- True+--+-- >>> example (star "aa") (star "aaa")+-- ^(aa)*$+-- ^(aaa)*$+-- False+--+-- >>> example (star "aa" <> star "aaa") (star "aaa" <> star "aa")+-- ^(aa)*(aaa)*$+-- ^(aaa)*(aa)*$+-- True+--+-- >>> example (star ("a" \/ "b")) (star $ star "a" <> star "b")+-- ^[a-b]*$+-- ^(a*b*)*$+-- True+--+equivalent :: forall c. (Ord c, Enum c, Bounded c) => RE c -> RE c -> Bool+equivalent x0 y0 = go mempty [(x0, y0)] where+    go :: Set (RE c, RE c) -> [(RE c, RE c)] -> Bool+    go !_ [] = True+    go acc (p@(x, y) : zs)+        | p `Set.member` acc = go acc zs+        -- if two regexps are structurally the same, we don't need to recurse.+        | x == y             = go (Set.insert p acc) zs+        | all agree ps       = go (Set.insert p acc) (ps ++ zs)+        | otherwise = False+      where+        cs = toList $ P.examples $ leadingChars x `P.wedge` leadingChars y+        ps = map (\c -> (derivate c x, derivate c y)) cs++    agree :: (RE c, RE c) -> Bool+    agree (x, y) = nullable x == nullable y++instance (Ord c, Enum c, Bounded c) => C.Equivalent c (RE c) where+    equivalent = equivalent++-------------------------------------------------------------------------------+-- Generation+-------------------------------------------------------------------------------++-- | Generate random strings of the language @RE c@ describes.+--+-- >>> let example = traverse_ print . take 3 . generate (curry QC.choose) 42+-- >>> example "abc"+-- "abc"+-- "abc"+-- "abc"+--+-- >>> example $ star $ "a" \/ "b"+-- "aaaaba"+-- "bbba"+-- "abbbbaaaa"+--+-- >>> example empty+--+-- prop> all (match r) $ take 10 $ generate (curry QC.choose) 42 (r :: RE Char)+--+generate+    :: (c -> c -> QC.Gen c) -- ^ character range generator+    -> Int    -- ^ seed+    -> RE c+    -> [[c]]  -- ^ infinite list of results+generate c seed re+    | isEmpty re = []+    | otherwise  = QC.unGen (QC.infiniteListOf (generator c re)) (QC.mkQCGen seed) 10++generator+    :: (c -> c -> QC.Gen c)+    -> RE c+    -> QC.Gen [c]+generator c = go where+    go (REChars cs)    = goChars cs+    go (REAppend rs)   = concat <$> traverse go rs+    go (REUnion cs rs)+        | RSet.null  cs = QC.oneof [ go r | r <- toList rs ]+        | otherwise     = QC.oneof $ goChars cs : [ go r | r <- toList rs ]+    go (REStar r)      = QC.sized $ \n -> do+        n' <- QC.choose (0, n)+        concat <$> sequence (replicate n' (go r))++    goChars cs = pure <$> QC.oneof [ c x y | (x,y) <- RSet.toRangeList cs ]++-------------------------------------------------------------------------------+-- Instances+-------------------------------------------------------------------------------++instance Eq c => Semigroup (RE c) where+    r <> r' = appends [r, r']++instance Eq c => Monoid (RE c) where+    mempty  = eps+    mappend = (<>)+    mconcat = appends++++instance c ~ Char => IsString (RE c) where+    fromString = string++instance (Ord c, Enum c, Bounded c, QC.Arbitrary c) => QC.Arbitrary (RE c) where+    arbitrary = QC.sized arb where+        c :: QC.Gen (RE c)+        c = REChars . RSet.fromRangeList <$> QC.arbitrary++        arb :: Int -> QC.Gen (RE c)+        arb n | n <= 0    = QC.oneof [c, fmap char QC.arbitrary, pure eps]+              | otherwise = QC.oneof+            [ c+            , pure eps+            , fmap char QC.arbitrary+            , liftA2 (<>) (arb n2) (arb n2)+            , liftA2 (\/) (arb n2) (arb n2)+            , fmap star (arb n2)+            ]+          where+            n2 = n `div` 2++    shrink (REUnion _cs rs) = Set.toList rs+    shrink (REAppend rs)    = rs ++ map appends (QC.shrink rs)+    shrink (REStar r)       = r : map star (QC.shrink r)+    shrink _                = []++instance (QC.CoArbitrary c) => QC.CoArbitrary (RE c) where+    coarbitrary (REChars cs)    = QC.variant (0 :: Int) . QC.coarbitrary (RSet.toRangeList cs)+    coarbitrary (REAppend rs)   = QC.variant (1 :: Int) . QC.coarbitrary rs+    coarbitrary (REUnion cs rs) = QC.variant (2 :: Int) . QC.coarbitrary (RSet.toRangeList cs, Set.toList rs)+    coarbitrary (REStar r)      = QC.variant (3 :: Int) . QC.coarbitrary r++-------------------------------------------------------------------------------+-- JavaScript+-------------------------------------------------------------------------------++instance c ~ Char => Pretty (RE c) where+    prettyS x = showChar '^' . go False x . showChar '$'+      where+        go :: Bool -> RE Char -> ShowS+        go p (REStar a)+            = parens p+            $ go True a . showChar '*'+        go p (REAppend rs)+            = parens p $ goMany id rs+        go p (REUnion cs rs)+            | RSet.null cs = goUnion p rs+            | Set.null rs  = prettyS cs+            | otherwise    = goUnion p (Set.insert (REChars cs) rs)+        go _ (REChars cs)+            = prettyS cs++        goUnion p rs+            | Set.member eps rs = parens p $ goUnion' True . showChar '?'+            | otherwise         = goUnion' p+          where+            goUnion' p' = case Set.toList (Set.delete eps rs) of+                [] -> go True empty+                [r] -> go p' r+                (r:rs') -> parens True $ goSome1 (showChar '|') r rs'++        goMany :: ShowS -> [RE Char] -> ShowS+        goMany sep = foldr (\a b -> go False a . sep . b) id++        goSome1 :: ShowS -> RE Char -> [RE Char] -> ShowS+        goSome1 sep r = foldl (\a b -> a . sep . go False b) (go False r)++        parens :: Bool -> ShowS -> ShowS+        parens True  s = showString "(" . s . showChar ')'+        parens False s = s++-------------------------------------------------------------------------------+-- Latin1+-------------------------------------------------------------------------------++instance C.ToLatin1 RE where+    toLatin1 (REChars rs)    = C.fromRSet (C.toLatin1 rs)+    toLatin1 (REAppend xs)   = appends (map C.toLatin1 xs)+    toLatin1 (REUnion rs xs) = C.fromRSet (C.toLatin1 rs) \/ unions (map C.toLatin1 (Set.toList  xs))+    toLatin1 (REStar r)      = star (C.toLatin1 r)++-------------------------------------------------------------------------------+-- Doctest+-------------------------------------------------------------------------------++-- $setup+-- >>> :set -XOverloadedStrings+-- >>> import Control.Monad (void)+-- >>> import Data.Foldable (traverse_)+-- >>> import Data.List (sort)+-- >>> import Data.Maybe (isJust)+--+-- >>> import Test.QuickCheck ((===))+-- >>> import qualified Test.QuickCheck as QC+--+-- >>> import Kleene.Classes (match)+-- >>> import Algebra.Lattice (bottom)+-- >>> import Kleene.RE ()+--+-- >>> let asREChar :: RE Char -> RE Char; asREChar = id
src/Kleene/Monad.hs view
@@ -13,8 +13,9 @@     -- | Binary operators are     --     -- * '<>' for append-    -- * '\/' for union     --+    -- There are no binary operator for union. Use 'unions'.+    --     empty,     eps,     char,@@ -38,8 +39,8 @@  import Prelude () import Prelude.Compat+import Data.Semigroup (Semigroup (..)) -import Algebra.Lattice     (BoundedJoinSemiLattice (..), JoinSemiLattice (..)) import Control.Applicative (liftA2) import Control.Monad       (ap) import Data.Foldable       (toList)@@ -98,10 +99,7 @@ -- >>> putPretty (empty :: M Bool) -- ^[]$ ----- >>> putPretty (bottom :: M Bool)--- ^[]$------ prop> match (empty :: M Bool) (s :: String) === False+-- prop> match (empty :: M Char) (s :: String) === False -- empty :: M c empty = MChars []@@ -114,7 +112,7 @@ -- >>> putPretty (mempty :: M Bool) -- ^$ ----- prop> match (eps :: M Bool) s === null (s :: String)+-- prop> match (eps :: M Char) s === null (s :: String) -- eps :: M c eps = MAppend []@@ -208,14 +206,16 @@ string [c] = MChars [c] string cs  = MAppend $ map (MChars . pure) cs -instance C.Kleene c (M c) where+instance C.Kleene  (M c) where     empty      = empty     eps        = eps-    char       = char     appends    = appends     unions     = unions     star       = star +instance C.CharKleene c (M c) where+    char       = char+ ------------------------------------------------------------------------------- -- derivative -------------------------------------------------------------------------------@@ -245,7 +245,7 @@ -- >>> putPretty $ derivate 'f' "foobar" -- ^oobar$ ----- >>> putPretty $ derivate 'x' $ "xyz" \/ "abc"+-- >>> putPretty $ derivate 'x' $ unions ["xyz", "abc"] -- ^yz$ -- -- >>> putPretty $ derivate 'x' $ star "xyz"@@ -305,7 +305,7 @@ -- "abc" -- "abc" ----- >>> example $ star $ "a" \/ "b"+-- >>> example $ star $ unions ["a", "b"] -- "ababbb" -- "baab" -- "abbababaa"@@ -348,7 +348,7 @@ -- >>> putPretty (toKleene re :: RE Char) -- ^[a-z]$ ---toKleene :: C.Kleene c k => M c -> k+toKleene :: C.CharKleene c k => M c -> k toKleene (MChars cs)    = C.oneof cs toKleene (MAppend rs)   = C.appends (map toKleene rs) toKleene (MUnion cs rs) = C.unions (C.oneof cs : map toKleene rs)@@ -366,12 +366,6 @@     mappend = (<>)     mconcat = appends -instance JoinSemiLattice (M c) where-    r \/ r' = unions [r, r']--instance BoundedJoinSemiLattice (M c) where-    bottom = empty- instance c ~ Char => IsString (M c) where     fromString = string @@ -387,7 +381,7 @@             , pure eps             , fmap char QC.arbitrary             , liftA2 (<>) (arb n2) (arb n2)-            , liftA2 (\/) (arb n2) (arb n2)+            , liftA2 (\x y -> unions [x,y]) (arb n2) (arb n2)             , fmap star (arb n2)             ]           where
src/Kleene/RE.hs view
@@ -1,9 +1,3 @@-{-# LANGUAGE BangPatterns           #-}-{-# LANGUAGE FlexibleInstances      #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE GADTs                  #-}-{-# LANGUAGE Safe                   #-}-{-# LANGUAGE ScopedTypeVariables    #-} module Kleene.RE (     RE (..),     -- * Construction@@ -34,570 +28,9 @@     generate,     -- * Other     isEmpty,+    nullableProof,     ) where -import Prelude ()-import Prelude.Compat--import Algebra.Lattice     (BoundedJoinSemiLattice (..), JoinSemiLattice (..))-import Control.Applicative (liftA2)-import Data.Foldable       (toList)-import Data.List           (foldl')-import Data.Map            (Map)-import Data.RangeSet.Map   (RSet)-import Data.Set            (Set)-import Data.String         (IsString (..))--import qualified Data.Function.Step.Discrete.Closed as SF-import qualified Data.Map                           as Map-import qualified Data.RangeSet.Map                  as RSet-import qualified Data.Set                           as Set-import qualified Test.QuickCheck                    as QC-import qualified Test.QuickCheck.Gen                as QC (unGen)-import qualified Test.QuickCheck.Random             as QC (mkQCGen)--import qualified Kleene.Classes            as C-import qualified Kleene.Internal.Partition as P-import           Kleene.Internal.Pretty---- | Regular expression------ Constructors are exposed, but you should use--- smart constructors in this module to construct 'RE'.------ The 'Eq' and 'Ord' instances are structural.--- The 'Kleene' etc constructors do "weak normalisation", so for values--- constructed using those operations 'Eq' witnesses "weak equivalence".--- See 'equivalent' for regular-expression equivalence.------ Structure is exposed in "Kleene.RE" module but consider constructors as--- half-internal.  There are soft-invariants, but violating them shouldn't--- break anything in the package. (e.g. 'transitionMap' will eventually--- terminate, but may create more redundant states if starting regexp is not--- "weakly normalised").----data RE c-    = REChars (RSet c)               -- ^ Single character-    | REAppend [RE c]                -- ^ Concatenation-    | REUnion (RSet c) (Set (RE c))  -- ^ Union-    | REStar (RE c)                  -- ^ Kleene star-  deriving (Eq, Ord, Show)------------------------------------------------------------------------------------ Smart constructor------------------------------------------------------------------------------------ | Empty regex. Doesn't accept anything.------ >>> putPretty (empty :: RE Char)--- ^[]$------ >>> putPretty (bottom :: RE Char)--- ^[]$------ prop> match (empty :: RE Char) (s :: String) === False----empty :: RE c-empty = REChars RSet.empty---- | Everything.------ >>> putPretty everything--- ^[^]*$------ prop> match (everything :: RE Char) (s :: String) === True----everything :: Bounded c => RE c-everything = REStar (REChars RSet.full)---- | Empty string. /Note:/ different than 'empty'.------ >>> putPretty eps--- ^$------ >>> putPretty (mempty :: RE Char)--- ^$------ prop> match (eps :: RE Char) s === null (s :: String)----eps :: RE c-eps = REAppend []---- |------ >>> putPretty (char 'x')--- ^x$----char :: c -> RE c-char = REChars . RSet.singleton---- |------ >>> putPretty $ charRange 'a' 'z'--- ^[a-z]$----charRange :: Ord c => c -> c -> RE c-charRange c c' = REChars $ RSet.singletonRange (c, c')---- | Any character. /Note:/ different than dot!------ >>> putPretty anyChar--- ^[^]$----anyChar :: Bounded c => RE c-anyChar = REChars RSet.full---- | Concatenate regular expressions.------ prop> (asREChar r <> s) <> t === r <> (s <> t)------ prop> asREChar r <> empty === empty--- prop> empty <> asREChar r === empty------ prop> asREChar r <> eps === r--- prop> eps <> asREChar r === r----appends :: Eq c => [RE c] -> RE c-appends rs0-    | elem empty rs1 = empty-    | otherwise = case rs1 of-        [r] -> r-        rs  -> REAppend rs-  where-    -- flatten one level of REAppend-    rs1 = concatMap f rs0--    f (REAppend rs) = rs-    f r             = [r]---- | Union of regular expressions.------ prop> asREChar r \/ r === r--- prop> asREChar r \/ s === s \/ r--- prop> (asREChar r \/ s) \/ t === r \/ (s \/ t)------ prop> empty \/ asREChar r === r--- prop> asREChar r \/ empty === r------ prop> everything \/ asREChar r === everything--- prop> asREChar r \/ everything === everything----unions :: (Ord c, Enum c, Bounded c) => [RE c] -> RE c-unions = uncurry mk . foldMap f where-    mk cs rss-        | Set.null rss = REChars cs-        | Set.member everything rss = everything-        | RSet.null cs = case Set.toList rss of-            []  -> empty-            [r] -> r-            _   -> REUnion cs rss-        | otherwise    = REUnion cs rss--    f (REUnion cs rs) = (cs, rs)-    f (REChars cs)    = (cs, Set.empty)-    f r               = (mempty, Set.singleton r)---- | Kleene star.------ prop> star (star r) === star (asREChar r)------ prop> star eps     === asREChar eps--- prop> star empty   === asREChar eps--- prop> star anyChar === asREChar everything------ prop> star (r      \/ eps) === star (asREChar r)--- prop> star (char c \/ eps) === star (asREChar (char c))--- prop> star (empty  \/ eps) === asREChar eps----star :: Ord c => RE c -> RE c-star r = case r of-    REStar _                          -> r-    REAppend []                       -> eps-    REChars cs | RSet.null cs         -> eps-    REUnion cs rs | Set.member eps rs -> case Set.toList rs' of-        []                  -> star (REChars cs)-        [r'] | RSet.null cs -> star r'-        _                   -> REStar (REUnion cs rs')-      where-        rs' = Set.delete eps rs-    _                                 -> REStar r---- | Literal string.------ >>> putPretty ("foobar" :: RE Char)--- ^foobar$------ >>> putPretty ("(.)" :: RE Char)--- ^\(\.\)$----string :: [c] -> RE c-string []  = eps-string [c] = REChars (RSet.singleton c)-string cs  = REAppend $ map (REChars . RSet.singleton) cs--instance (Ord c, Enum c, Bounded c) => C.Kleene c (RE c) where-    empty      = empty-    eps        = eps-    char       = char-    appends    = appends-    unions     = unions-    star       = star--instance (Ord c, Enum c, Bounded c) => C.FiniteKleene c (RE c) where-    everything = everything-    charRange  = charRange-    fromRSet   = REChars-    anyChar    = anyChar------------------------------------------------------------------------------------ derivative------------------------------------------------------------------------------------ | We say that a regular expression r is nullable if the language it defines--- contains the empty string.------ >>> nullable eps--- True------ >>> nullable (star "x")--- True------ >>> nullable "foo"--- False----nullable :: RE c -> Bool-nullable (REChars _)      = False-nullable (REAppend rs)    = all nullable rs-nullable (REUnion _cs rs) = any nullable rs-nullable (REStar _)       = True---- | Intuitively, the derivative of a language \(\mathcal{L} \subset \Sigma^\star\)--- with respect to a symbol \(a \in \Sigma\) is the language that includes only--- those suffixes of strings with a leading symbol \(a\) in \(\mathcal{L}\).------ >>> putPretty $ derivate 'f' "foobar"--- ^oobar$------ >>> putPretty $ derivate 'x' $ "xyz" \/ "abc"--- ^yz$------ >>> putPretty $ derivate 'x' $ star "xyz"--- ^yz(xyz)*$----derivate :: (Ord c, Enum c, Bounded c) => c -> RE c -> RE c-derivate c (REChars cs)     = derivateChars c cs-derivate c (REUnion cs rs)  = unions $ derivateChars c cs : [ derivate c r | r <- toList rs]-derivate c (REAppend rs)    = derivateAppend c rs-derivate c rs@(REStar r)    = derivate c r <> rs--derivateAppend :: (Ord c, Enum c, Bounded c) => c -> [RE c] -> RE c-derivateAppend _ []      = empty-derivateAppend c [r]     = derivate c r-derivateAppend c (r:rs)-    | nullable r         = unions [r' <> appends rs, rs']-    | otherwise          = r' <> appends rs-  where-    r'  = derivate c r-    rs' = derivateAppend c rs--derivateChars :: Ord c =>  c -> RSet c -> RE c-derivateChars c cs-    | c `RSet.member` cs      = eps-    | otherwise               = empty--instance (Ord c, Enum c, Bounded c) => C.Derivate c (RE c) where-    nullable = nullable-    derivate = derivate--instance (Ord c, Enum c, Bounded c) => C.Match c (RE c) where-    match r = nullable . foldl' (flip derivate) r------------------------------------------------------------------------------------ isEmpty------------------------------------------------------------------------------------ | Whether 'RE' is (structurally) equal to 'empty'.------ prop> isEmpty r === all (not . nullable) (Map.keys $ transitionMap $ asREChar r)-isEmpty :: RE c -> Bool-isEmpty (REChars rs) = RSet.null rs-isEmpty _            = False------------------------------------------------------------------------------------ States------------------------------------------------------------------------------------ | Transition map. Used to construct 'Kleene.DFA.DFA'.------ >>> void $ Map.traverseWithKey (\k v -> putStrLn $ pretty k ++ " : " ++ SF.showSF (fmap pretty v)) $ transitionMap ("ab" :: RE Char)--- ^[]$ : \_ -> "^[]$"--- ^b$ : \x -> if---     | x <= 'a'  -> "^[]$"---     | x <= 'b'  -> "^$"---     | otherwise -> "^[]$"--- ^$ : \_ -> "^[]$"--- ^ab$ : \x -> if---     | x <= '`'  -> "^[]$"---     | x <= 'a'  -> "^b$"---     | otherwise -> "^[]$"----transitionMap-    :: forall c. (Ord c, Enum c, Bounded c)-    => RE c-    -> Map (RE c) (SF.SF c (RE c))-transitionMap re = go Map.empty [re] where-    go :: Map (RE c) (SF.SF c (RE c))-       -> [RE c]-       -> Map (RE c) (SF.SF c (RE c))-    go !acc [] = acc-    go acc (r : rs)-        | r `Map.member` acc = go acc rs-        | otherwise = go (Map.insert r pm acc) (SF.values pm ++ rs)-      where-        pm = P.toSF (\c -> derivate c r) (leadingChars r)--instance (Ord c, Enum c, Bounded c) => C.TransitionMap c (RE c) where-    transitionMap = transitionMap---- | Leading character sets of regular expression.------ >>> leadingChars "foo"--- fromSeparators "ef"------ >>> leadingChars (star "b" <> star "e")--- fromSeparators "abde"------ >>> leadingChars (charRange 'b' 'z')--- fromSeparators "az"----leadingChars :: (Ord c, Enum c, Bounded c) => RE c -> P.Partition c-leadingChars (REChars cs)    = P.fromRSet cs-leadingChars (REUnion cs rs) = P.fromRSet cs <> foldMap leadingChars rs-leadingChars (REStar r)      = leadingChars r-leadingChars (REAppend rs)   = leadingCharsAppend rs--leadingCharsAppend :: (Ord c, Enum c, Bounded c) => [RE c] -> P.Partition c-leadingCharsAppend [] = P.whole-leadingCharsAppend (r : rs)-    | nullable r = leadingChars r <> leadingCharsAppend rs-    | otherwise  = leadingChars r------------------------------------------------------------------------------------ Equivalence------------------------------------------------------------------------------------ | Whether two regexps are equivalent.------ @--- 'equivalent' re1 re2 <=> forall s. 'match' re1 s === 'match' re1 s--- @------ >>> let re1 = star "a" <> "a"--- >>> let re2 = "a" <> star "a"------ These are different regular expressions, even we perform--- some normalisation-on-construction:------ >>> re1 == re2--- False------ They are however equivalent:------ >>> equivalent re1 re2--- True------ The algorithm works by executing 'states' on "product" regexp,--- and checking whether all resulting states are both accepting or rejecting.------ @--- re1 == re2 ==> 'equivalent' re1 re2--- @------ === More examples------ >>> let example re1 re2 = putPretty re1 >> putPretty re2 >> return (equivalent re1 re2)--- >>> example re1 re2--- ^a*a$--- ^aa*$--- True------ >>> example (star "aa") (star "aaa")--- ^(aa)*$--- ^(aaa)*$--- False------ >>> example (star "aa" <> star "aaa") (star "aaa" <> star "aa")--- ^(aa)*(aaa)*$--- ^(aaa)*(aa)*$--- True------ >>> example (star ("a" \/ "b")) (star $ star "a" <> star "b")--- ^[a-b]*$--- ^(a*b*)*$--- True----equivalent :: forall c. (Ord c, Enum c, Bounded c) => RE c -> RE c -> Bool-equivalent x0 y0 = go mempty [(x0, y0)] where-    go :: Set (RE c, RE c) -> [(RE c, RE c)] -> Bool-    go !_ [] = True-    go acc (p@(x, y) : zs)-        | p `Set.member` acc = go acc zs-        -- if two regexps are structurally the same, we don't need to recurse.-        | x == y             = go (Set.insert p acc) zs-        | all agree ps       = go (Set.insert p acc) (ps ++ zs)-        | otherwise = False-      where-        cs = toList $ P.examples $ leadingChars x `P.wedge` leadingChars y-        ps = map (\c -> (derivate c x, derivate c y)) cs--    agree :: (RE c, RE c) -> Bool-    agree (x, y) = nullable x == nullable y--instance (Ord c, Enum c, Bounded c) => C.Equivalent c (RE c) where-    equivalent = equivalent------------------------------------------------------------------------------------ Generation------------------------------------------------------------------------------------ | Generate random strings of the language @RE c@ describes.------ >>> let example = traverse_ print . take 3 . generate (curry QC.choose) 42--- >>> example "abc"--- "abc"--- "abc"--- "abc"------ >>> example $ star $ "a" \/ "b"--- "aaaaba"--- "bbba"--- "abbbbaaaa"------ >>> example empty------ prop> all (match r) $ take 10 $ generate (curry QC.choose) 42 (r :: RE Char)----generate-    :: (c -> c -> QC.Gen c) -- ^ character range generator-    -> Int    -- ^ seed-    -> RE c-    -> [[c]]  -- ^ infinite list of results-generate c seed re-    | isEmpty re = []-    | otherwise  = QC.unGen (QC.infiniteListOf (generator c re)) (QC.mkQCGen seed) 10--generator-    :: (c -> c -> QC.Gen c)-    -> RE c-    -> QC.Gen [c]-generator c = go where-    go (REChars cs)    = goChars cs-    go (REAppend rs)   = concat <$> traverse go rs-    go (REUnion cs rs)-        | RSet.null  cs = QC.oneof [ go r | r <- toList rs ]-        | otherwise     = QC.oneof $ goChars cs : [ go r | r <- toList rs ]-    go (REStar r)      = QC.sized $ \n -> do-        n' <- QC.choose (0, n)-        concat <$> sequence (replicate n' (go r))--    goChars cs = pure <$> QC.oneof [ c x y | (x,y) <- RSet.toRangeList cs ]------------------------------------------------------------------------------------ Instances----------------------------------------------------------------------------------instance Eq c => Semigroup (RE c) where-    r <> r' = appends [r, r']--instance Eq c => Monoid (RE c) where-    mempty  = eps-    mappend = (<>)-    mconcat = appends--instance (Ord c, Enum c, Bounded c) => JoinSemiLattice (RE c) where-    r \/ r' = unions [r, r']--instance (Ord c, Enum c, Bounded c) => BoundedJoinSemiLattice (RE c) where-    bottom = empty--instance c ~ Char => IsString (RE c) where-    fromString = string--instance (Ord c, Enum c, Bounded c, QC.Arbitrary c) => QC.Arbitrary (RE c) where-    arbitrary = QC.sized arb where-        c :: QC.Gen (RE c)-        c = REChars . RSet.fromRangeList <$> QC.arbitrary--        arb :: Int -> QC.Gen (RE c)-        arb n | n <= 0    = QC.oneof [c, fmap char QC.arbitrary, pure eps]-              | otherwise = QC.oneof-            [ c-            , pure eps-            , fmap char QC.arbitrary-            , liftA2 (<>) (arb n2) (arb n2)-            , liftA2 (\/) (arb n2) (arb n2)-            , fmap star (arb n2)-            ]-          where-            n2 = n `div` 2--instance (QC.CoArbitrary c) => QC.CoArbitrary (RE c) where-    coarbitrary (REChars cs)    = QC.variant (0 :: Int) . QC.coarbitrary (RSet.toRangeList cs)-    coarbitrary (REAppend rs)   = QC.variant (1 :: Int) . QC.coarbitrary rs-    coarbitrary (REUnion cs rs) = QC.variant (2 :: Int) . QC.coarbitrary (RSet.toRangeList cs, Set.toList rs)-    coarbitrary (REStar r)      = QC.variant (3 :: Int) . QC.coarbitrary r------------------------------------------------------------------------------------ JavaScript----------------------------------------------------------------------------------instance c ~ Char => Pretty (RE c) where-    prettyS x = showChar '^' . go False x . showChar '$'-      where-        go :: Bool -> RE Char -> ShowS-        go p (REStar a)-            = parens p-            $ go True a . showChar '*'-        go p (REAppend rs)-            = parens p $ goMany id rs-        go p (REUnion cs rs)-            | RSet.null cs = goUnion p rs-            | Set.null rs  = prettyS cs-            | otherwise    = goUnion p (Set.insert (REChars cs) rs)-        go _ (REChars cs)-            = prettyS cs--        goUnion p rs-            | Set.member eps rs = parens p $ goUnion' True . showChar '?'-            | otherwise         = goUnion' p-          where-            goUnion' p' = case Set.toList (Set.delete eps rs) of-                [] -> go True empty-                [r] -> go p' r-                (r:rs') -> parens True $ goSome1 (showChar '|') r rs'--        goMany :: ShowS -> [RE Char] -> ShowS-        goMany sep = foldr (\a b -> go False a . sep . b) id--        goSome1 :: ShowS -> RE Char -> [RE Char] -> ShowS-        goSome1 sep r = foldl (\a b -> a . sep . go False b) (go False r)--        parens :: Bool -> ShowS -> ShowS-        parens True  s = showString "(" . s . showChar ')'-        parens False s = s------------------------------------------------------------------------------------ Doctest------------------------------------------------------------------------------------ $setup--- >>> :set -XOverloadedStrings--- >>> import Control.Monad (void)--- >>> import Data.Foldable (traverse_)--- >>> import Data.List (sort)------ >>> import Test.QuickCheck ((===))--- >>> import qualified Test.QuickCheck as QC------ >>> import Kleene.Classes (match)--- >>> let asREChar :: RE Char -> RE Char; asREChar = id+-- This to include orphans.+import Kleene.Internal.RE+import Kleene.DFA ()