ideas-0.6: src/Domain/RegularExpr/Expr.hs
{-# OPTIONS -XTypeSynonymInstances #-}
-----------------------------------------------------------------------------
-- Copyright 2010, Open Universiteit Nederland. This file is distributed
-- under the terms of the GNU General Public License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer : bastiaan.heeren@ou.nl
-- Stability : provisional
-- Portability : portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Domain.RegularExpr.Expr where
import Common.Rewriting
import Common.Traversable
import Common.Uniplate
import Control.Monad
import Domain.Math.Expr.Symbolic
import Test.QuickCheck
--------------------------------------------------------------------
-- Data type declaration
infixl 4 :|:
infixl 5 :*:
type RegExp = RE String
data RE a = EmptySet | Epsilon | Atom a | Option (RE a) | Star (RE a)
| Plus (RE a) | RE a :*: RE a | RE a :|: RE a
deriving (Show, Eq, Ord)
--------------------------------------------------------------------
-- Fold
foldRE (es, eps, at, opt, st, pl, sq, ch) = rec
where
rec regexp =
case regexp of
EmptySet -> es
Epsilon -> eps
Atom a -> at a
Option r -> opt (rec r)
Star r -> st (rec r)
Plus r -> pl (rec r)
r :*: s -> sq (rec r) (rec s)
r :|: s -> ch (rec r) (rec s)
--------------------------------------------------------------------
-- General instances
instance Functor RE where
fmap f = foldRE (EmptySet, Epsilon, Atom . f, Option, Star, Plus, (:*:), (:|:))
instance Crush RE where
crush (Atom a) = [a]
crush regexp = concatMap crush (children regexp)
instance Arbitrary RegExp where
arbitrary = sized (arbRE $ oneof $ map return ["a", "b", "c", "d"])
instance CoArbitrary RegExp where
coarbitrary = foldRE
( variant 0
, variant 1
, \a -> variant 2 . coarbitrary a
, \a -> variant 3 . a
, \a -> variant 4 . a
, \a -> variant 5 . a
, \a b -> variant 6 . a . b
, \a b -> variant 7 . a . b
)
arbRE :: Gen a -> Int -> Gen (RE a)
arbRE g n
| n == 0 = frequency
[ (6, liftM Atom g)
, (3, return Epsilon)
, (1, return EmptySet)
]
| otherwise = frequency
[ (3, arbRE g 0)
, (2, unop Star) -- (1, unop Option), (1, unop Plus)
, (3, binop (:*:)), (3, binop (:|:))
]
where
rec = arbRE g (n `div` 2)
unop f = liftM f rec
binop f = liftM2 f rec rec
--------------------------------------------------------------------
-- Pretty-printer
ppRegExp :: RegExp -> String
ppRegExp = ppWith (const id)
ppWith :: (Int -> a -> String) -> RE a -> String
ppWith f = ($ 0) . foldRE
(const "F", const "T", flip f, unop "?", unop "*", unop "+", binop 5 "", binop 4 "|")
where
unop s a _ = parIf False (a 6 ++ s)
binop i s a b n = parIf (n > i) (a i ++ s ++ b i)
parIf b s = if b then "(" ++ s ++ ")" else s
--testje = ppWith (const id) (Star (Plus (Atom "P")) :*: (Option (Atom "Q" :*: Option (Atom "S")) :|: Atom "R"))
--------------------------------------------------------------------
-- Function for associative operators
concatOp :: Operator (RE a)
concatOp = associativeOperator (:*:) isConcat
where
isConcat (r :*: s) = Just (r, s)
isConcat _ = Nothing
choiceOp :: Operator (RE a)
choiceOp = associativeOperator (:|:) isChoice
where
isChoice (r :|: s) = Just (r, s)
isChoice _ = Nothing
--------------------------------------------------------------------
-- Instances for rewriting
instance Uniplate (RE a) where
uniplate regexp =
case regexp of
EmptySet -> ([], \[] -> EmptySet)
Epsilon -> ([], \[] -> Epsilon)
Atom a -> ([], \[] -> Atom a)
Option r -> ([r], \[a] -> Option a)
Star r -> ([r], \[a] -> Star a)
Plus r -> ([r], \[a] -> Plus a)
r :*: s -> ([r, s], \[a, b] -> a :*: b)
r :|: s -> ([r, s], \[a, b] -> a :|: b)
instance Eq a => ShallowEq (RE a) where
shallowEq re1 re2 =
case (re1, re2) of
(EmptySet, EmptySet) -> True
(Epsilon, Epsilon ) -> True
(Atom a, Atom b ) -> a==b
(Option _, Option _) -> True
(Star _, Star _ ) -> True
(Plus _, Plus _ ) -> True
(_ :*: _, _ :*: _ ) -> True
(_ :|: _, _ :|: _ ) -> True
_ -> False
instance Different (RE a) where
different = (EmptySet, Epsilon)
instance IsTerm RegExp where
toTerm = foldRE
( nullary "EmptySet", nullary "Epsilon", variable, unary "Option"
, unary "Star", unary "Plus", binary ":*:", binary ":|:"
)
fromTerm a = fromTermWith f a `mplus` liftM Atom (getVariable a)
where
f s []
| s == "EmptySet" = return EmptySet
| s == "Epsilon" = return Epsilon
f s [x]
| s == "Option" = return (Option x)
| s == "Star" = return (Star x)
| s == "Plus" = return (Plus x)
f s [x, y]
| s == ":*:" = return (x :*: y)
| s == ":|:" = return (x :|: y)
f _ _ = fail "fromExpr"
instance Rewrite RegExp where
operators = [concatOp, choiceOp]
associativeOps = const $ map toSymbol [":*:", ":|:"]