packages feed

regexp-tries-0.1.0: src/Data/ListTrie/RegExp/Common.hs

{-# LANGUAGE
        FlexibleContexts,
        FlexibleInstances,
        FunctionalDependencies,
        MultiParamTypeClasses
  #-}

{- | This module provides functionality that all regular expression
     implementations share.
-}

module Data.ListTrie.RegExp.Common
    ( WrapClass (..)

    -- * Expression Type for Combinators
    , Expr (..)
    , ExprClass (..)

    -- * Regular Expression Combinators
    , complement
    , intersection
    , union

    -- * Trie operations
    , LookupRegExpClass (..)
    , lookup
    , delete

    -- * Optimzation of combined Regular Expressions
    , OptimizeClass (..)
    , optimize
    )
where

import Prelude hiding (lookup)
import qualified Data.KeyMap as KM
import Data.ListTrie.ListTrie hiding
    ( delete
    , intersection
    , lookup
    , union
    )
import Data.Map (Map)

-- Wrapping and unwrapping of StringTrie ---------------------------------------

{- | Allows the usage of 'StringTrie' and 'ListTrie' the same way.
-}
class (KeyMap [sym] map, KeyMap sym emap)
   => WrapClass map emap sym | map -> emap where
    wrap   :: ListTrie emap v -> map v
    unwrap :: map v -> ListTrie emap v

instance WrapClass StringTrie (Map Char) Char where
    wrap   = StringTrie
    unwrap = unStringTrie

instance (KeyMap sym emap) => WrapClass (ListTrie emap) emap sym where
    wrap   = id
    unwrap = id

-- Regular Expression Combinators ----------------------------------------------

{- | Expressions may be build by using the combinators 'complement',
     'intersection' and 'union'. They can then be used with 'lookup' and
     'delete'.
-}
data Expr regexp = REExpr regexp
                 | Complement (Expr regexp)
                 | Intersection (Expr regexp) (Expr regexp)
                 | Union (Expr regexp) (Expr regexp)

-- | Allows to also use regular expressions directly instead of 'Expr' types.
class ExprClass e regexp | e -> regexp where
    toExpr :: e -> (Expr regexp)

instance ExprClass (Expr regexp) regexp where
    toExpr = id

-- | Perform the operation with the complement of the expression.
complement
    :: ExprClass e regexp
    => e        -- ^ an expression
    -> Expr regexp -- ^ the complement
complement = Complement . toExpr

-- | Intersection of the result of two expressions.
intersection
    :: (ExprClass e1 regexp, ExprClass e2 regexp)
    => e1
    -> e2
    -> Expr regexp
intersection e1 e2 = Intersection (toExpr e1) (toExpr e2)

-- | Union of the result of two expressions.
union
    :: (ExprClass e1 regexp, ExprClass e2 regexp)
    => e1
    -> e2
    -> Expr regexp
union e1 e2 = (Union (toExpr e1) (toExpr e2))

-- Trie operations -------------------------------------------------------------

class (WrapClass map emap sym, OptimizeClass regexp)
   => LookupRegExpClass regexp map emap sym | regexp -> sym where
    lookupRegExp
        :: regexp
        -> map v
        -> map v
    lookupComplement
        :: regexp
        -> map v
        -> map v

-- | Lookup the given expression in the given Trie and return a Trie of results.
lookup
    :: (LookupRegExpClass (regexp sym) map emap sym, ExprClass e (regexp sym))
    => e     -- ^ expression to lookup
    -> map v -- ^ Trie on wich to perform the lookup
    -> map v -- ^ resulting Trie
lookup = lookupExpr . optimize

-- | Delete entries matching the expression from the given Trie.
delete
    :: (LookupRegExpClass (regexp sym) map emap sym, ExprClass e (regexp sym))
    => e     -- ^ expression
    -> map v -- ^ Trie to delete from
    -> map v -- ^ Trie missing the entries that matched the expression.
delete = lookupExpr . optimize . complement

-- | Evaluates the given expression on the given Trie.
lookupExpr
    :: LookupRegExpClass (regexp sym) map emap sym
    => Expr (regexp sym)
    -> map v
    -> map v
lookupExpr (REExpr re)                  t = lookupRegExp re t
lookupExpr (Complement (REExpr re))     t = lookupComplement re t
lookupExpr (Complement (Complement e))  t = lookupExpr e t
lookupExpr (Complement e)               t = difference t $ lookupExpr e t
lookupExpr (Intersection e1 e2)         t = lookupExpr e2 (lookupExpr e1 t)
lookupExpr (Union e1 e2)                t = KM.union t1 t2
    where
        t1 = lookupExpr e1 t
        t2 = lookupExpr e2 t

-- Optimzation of combined Regular Expressions ---------------------------------

class OptimizeClass regexp where
    -- | Optimize the given expression.
    optimizeExpr
        :: Expr regexp -- ^ expression
        -> Expr regexp -- ^ optimized expression

-- | Optimize the given expression.
optimize
    :: (ExprClass e regexp, OptimizeClass regexp)
    => e           -- ^ expression
    -> Expr regexp -- ^ optimized expression
optimize = optimizeExpr . toExpr