packages feed

MicroCabal-0.2.1.0: src/MicroCabal/Regex.hs

-- Originally stolen from https://crypto.stanford.edu/~blynn/haskell/re.html

-- Regular expression matching using Brzozowski's algorithm
module MicroCabal.Regex(CharClass(..), Regex(..), eps, regexMatch) where
import Data.List(sort, nub)

data CharClass = Pos String | Neg String
  deriving (Eq, Ord, Show)

elemCC :: Char -> CharClass -> Bool
elemCC c (Pos cs) = c `elem` cs
elemCC c (Neg cs) = c `notElem` cs

data Regex
  = Lit CharClass
  | Seq Regex Regex
  | Star Regex
  | Or [Regex]
  | And [Regex]
  | Not Regex
  deriving (Eq, Ord, Show)

regexMatch :: Regex -> String -> Bool
regexMatch re ""    = nullable re
regexMatch re (c:s) = regexMatch (derive c re) s

-- The regex `()`. The language containing only the empty string.
eps :: Regex
eps = Star noGood

-- The regex `[]`. The empty language.
noGood :: Regex
noGood = Lit $ Pos []

-- The regex `.*`. The language containing everything.
allGood :: Regex
allGood = Star $ Lit $ Neg []

nullable :: Regex -> Bool
nullable re =
  case re of
    Lit _   -> False
    Star _  -> True
    Seq r s -> nullable r && nullable s
    Or  rs  -> any nullable rs
    And rs  -> all nullable rs
    Not r   -> not $ nullable r

derive :: Char -> Regex -> Regex
derive c re =
  case re of
    Lit cc | elemCC c cc   -> eps
           | otherwise     -> noGood
    Star r                 -> derive c r `mkSeq` mkStar r
    r `Seq` s | nullable r -> mkOr [derive c r `mkSeq` s, derive c s]
              | otherwise  -> derive c r `mkSeq` s
    And rs                 -> mkAnd $ map (derive c) rs
    Or  rs                 -> mkOr  $ map (derive c) rs
    Not r                  -> mkNot $ derive c r

-- Smart constructors
mkSeq :: Regex -> Regex -> Regex
mkSeq r s
  | r == noGood || s == noGood = noGood
  | r == eps       = s
  | s == eps       = r
  | x `Seq` y <- r = x `mkSeq` (y `mkSeq` s)
  | otherwise      = r `Seq` s

mkOr :: [Regex] -> Regex
mkOr xs
  | allGood `elem` zs = allGood
  | null zs           = noGood
  | [z] <- zs         = z
  | otherwise         = Or zs
  where
    zs = nub $ sort $ filter (/= noGood) flat
    flat           = concatMap deOr xs
    deOr (Or rs)   = rs
    deOr r         = [r]

mkAnd :: [Regex] -> Regex
mkAnd xs
  | noGood `elem` zs = noGood
  | null zs          = allGood
  | [z] <- zs        = z
  | otherwise        = And zs
  where
    zs = nub $ sort $ filter (/= allGood) flat
    flat             = concatMap deAnd xs
    deAnd (And rs)   = rs
    deAnd r          = [r]

mkStar :: Regex -> Regex
mkStar (Star s) = mkStar s
mkStar r        = Star r

mkNot :: Regex -> Regex
mkNot (Lit (Pos [])) = allGood
mkNot (Not s)        = s
mkNot r              = Not r