{- |
Neutral regular expressions
-}
{-# LANGUAGE CPP #-}
module FST.NReg (
  -- * Types
  NReg(..),

  -- * Conversion functions
  toRReg,
  toReg,
  nVarToSymbol
  ) where

#if MIN_VERSION_base(4,8,0)
import Prelude hiding ((<*>))
#endif
import Control.Monad
import FST.RegTypes
import FST.RRegTypes

-- | Neutral regular expressions
data NReg a = NCross      (NReg a) (NReg a)
            | NComp       (NReg a) (NReg a)
            | NUnion      (NReg a) (NReg a)
            | NProduct    (NReg a) (NReg a)
            | NIntersect  (NReg a) (NReg a)
            | NStar       (NReg a)
            | NComplement (NReg a)
            | NSymbol a
            | NRelation a a
            | NEpsilon
            | NEmptySet
            | NVar String
            | Fun String [NReg a]
            | NAll

-- | If possible, build a regular expression instead of a regular relation
toRReg :: Eq a => NReg a -> Maybe (RReg a)
toRReg reg = maybe (nRReg reg) (return . idR) (toReg reg)
 where
   nRReg :: Eq a => NReg a -> Maybe (RReg a)
   nRReg NEmptySet          = Just EmptyR
   nRReg (NRelation a b)    = Just (r a b)
   nRReg (NComp n1 n2)      = liftM2 (<.>) (toRReg n1) (toRReg n2)
   nRReg (NCross n1 n2)     = liftM2 (<*>) (toReg n1)  (toReg n2)
   nRReg (NUnion n1 n2)     = case (toRReg n1, toRReg n2) of
     (Just r1, Just r2) -> Just (r1 <|> r2)
     _                  -> fmap idR $ liftM2 (<|>) (toReg n1) (toReg n2)
   nRReg (NProduct n1 n2)   = case (toRReg n1, toRReg n2) of
     (Just r1,Just r2) -> Just (r1 |> r2)
     _                 -> fmap idR $ liftM2 (|>) (toReg n1) (toReg n2)
   nRReg (NStar n1)         = case toRReg n1 of
     Just r1 -> Just (star r1)
     _       -> liftM (idR . star) (toReg n1)
   nRReg (NIntersect n1 n2) = fmap idR $ liftM2 (<&>) (toReg n1) (toReg n2)
   nRReg (NComplement n1)   = fmap (idR . complement) (toReg n1)
   nRReg _                  = Nothing

-- | If possible, converts NReg to Reg
toReg :: Eq a => NReg a -> Maybe (Reg a)
toReg NEmptySet           = return empty
toReg NEpsilon            = return eps
toReg NAll                = return allS
toReg (NSymbol a)         = return (s a)
toReg (NStar n1)          = liftM  star       (toReg n1)
toReg (NComplement n1)    = liftM  complement (toReg n1)
toReg (NUnion n1 n2)      = liftM2 (<|>) (toReg n1) (toReg n2)
toReg (NIntersect n1 n2)  = liftM2 (<&>) (toReg n1) (toReg n2)
toReg (NProduct n1 n2)    = liftM2 (|>)  (toReg n1) (toReg n2)
toReg  _                  = Nothing

-- | Convert variables to symbols
nVarToSymbol :: NReg String -> NReg String
nVarToSymbol (NCross n1 n2)     = NCross      (nVarToSymbol n1) (nVarToSymbol n2)
nVarToSymbol (NComp n1 n2)      = NComp       (nVarToSymbol n1) (nVarToSymbol n2)
nVarToSymbol (NUnion n1 n2)     = NUnion      (nVarToSymbol n1) (nVarToSymbol n2)
nVarToSymbol (NProduct n1 n2)   = NProduct    (nVarToSymbol n1) (nVarToSymbol n2)
nVarToSymbol (NStar n1)         = NStar       (nVarToSymbol n1)
nVarToSymbol (NIntersect n1 n2) = NIntersect  (nVarToSymbol n1) (nVarToSymbol n2)
nVarToSymbol (NComplement n1)   = NComplement (nVarToSymbol n1)
nVarToSymbol (NVar str)         = NSymbol str
nVarToSymbol n1                 = n1