module Generics.BiGUL.PatternMatching where
import Generics.BiGUL
import Generics.BiGUL.Error
import GHC.InOut
import Control.Monad.Except
modifyError :: (e -> e) -> Either e a -> Either e a
modifyError f = either (Left . f) Right
deconstruct :: Pat a env con -> a -> Either PatError env
deconstruct PVar x = return (Var x)
deconstruct PVar' x = return (Var x)
deconstruct (PConst c) x = if c == x then return () else throwError PEConstantMismatch
deconstruct (l `PProd` r) (x, y) = liftM2 (,) (modifyError PEProdL (deconstruct l x))
(modifyError PEProdR (deconstruct r y))
deconstruct (PLeft p) (Left x) = modifyError PELeft (deconstruct p x)
deconstruct (PLeft _) _ = throwError PELeftMismatch
deconstruct (PRight p) (Right x) = modifyError PERight (deconstruct p x)
deconstruct (PRight _) _ = throwError PERightMismatch
deconstruct (PIn p) x = modifyError PEIn (deconstruct p (out x))
construct :: Pat a env con -> env -> a
construct PVar (Var x) = x
construct PVar' (Var x) = x
construct (PConst c) _ = c
construct (l `PProd` r) (x, y) = (construct l x, construct r y)
construct (PLeft p) x = Left (construct p x)
construct (PRight p) x = Right (construct p x)
construct (PIn p) x = inn (construct p x)
retrieve :: Direction env a -> env -> a
retrieve DVar (Var x ) = x
retrieve (DLeft d) (env, _) = retrieve d env
retrieve (DRight d) (_, env) = retrieve d env
eval :: Expr env a -> env -> a
eval (EDir d) env = retrieve d env
eval (EConst c) env = c
eval (l `EProd` r) env = (eval l env, eval r env)
eval (ELeft e) env = Left (eval e env)
eval (ERight e) env = Right (eval e env)
eval (EIn e) env = inn (eval e env)
uneval :: Pat a env con -> Expr env b -> b -> con -> Either PatError con
uneval p (EDir d) x con = unevalDir p d x con
uneval p (EConst c) x con = if c == x then return con else throwError PEConstantMismatch
uneval p (EProd l r) (x, y) con = modifyError PEProdL (uneval p l x con) >>= modifyError PEProdR . uneval p r y
uneval p (ELeft e) (Left x) con = modifyError PELeft (uneval p e x con)
uneval p (ELeft _) x con = throwError PELeftMismatch
uneval p (ERight e) (Right x) con = modifyError PERight (uneval p e x con)
uneval p (ERight _) x con = throwError PERightMismatch
uneval p (EIn e) x con = modifyError PEIn (uneval p e (out x) con)
unevalDir :: Pat a env con -> Direction env b -> b -> con -> Either PatError con
unevalDir PVar DVar x (Just y) = if x == y
then return (Just x)
else throwError PEIncompatibleUpdates
unevalDir PVar DVar x Nothing = return (Just x)
unevalDir PVar' DVar x (Just y) = throwError PEMultipleUpdates
unevalDir PVar' DVar x Nothing = return (Just x)
unevalDir (PConst c) _ x con = return con
unevalDir (l `PProd` r) (DLeft d) x (conl, conr) = liftM (, conr) (modifyError PEProdL (unevalDir l d x conl))
unevalDir (l `PProd` r) (DRight d) x (conl, conr) = liftM (conl ,) (modifyError PEProdR (unevalDir r d x conr))
unevalDir (PLeft p) d x con = modifyError PELeft (unevalDir p d x con)
unevalDir (PRight p) d x con = modifyError PERight (unevalDir p d x con)
unevalDir (PIn p) d x con = modifyError PEIn (unevalDir p d x con)
fromContainerV :: Pat v env con -> con -> Either PatError env
fromContainerV PVar Nothing = throwError PEValueUnrecoverable
fromContainerV PVar (Just v) = return (Var v)
fromContainerV PVar' Nothing = throwError PEValueUnrecoverable
fromContainerV PVar' (Just v) = return (Var v)
fromContainerV (PConst c) con = return ()
fromContainerV (l `PProd` r) (conl, conr) = liftM2 (,) (modifyError PEProdL (fromContainerV l conl))
(modifyError PEProdR (fromContainerV r conr))
fromContainerV (PLeft p) con = modifyError PELeft (fromContainerV p con)
fromContainerV (PRight p) con = modifyError PERight (fromContainerV p con)
fromContainerV (PIn p) con = modifyError PEIn (fromContainerV p con)
fromContainerS :: Pat s env con -> env -> con -> env
fromContainerS PVar (Var s) Nothing = (Var s )
fromContainerS PVar (Var s) (Just s') = (Var s')
fromContainerS PVar' (Var s) Nothing = (Var s )
fromContainerS PVar' (Var s) (Just s') = (Var s')
fromContainerS (PConst c) _ _ = ()
fromContainerS (l `PProd` r) (envl, envr) (conl, conr) = (fromContainerS l envl conl, fromContainerS r envr conr)
fromContainerS (PLeft p) env con = fromContainerS p env con
fromContainerS (PRight p) env con = fromContainerS p env con
fromContainerS (PIn p) env con = fromContainerS p env con
emptyContainer :: Pat v env con -> con
emptyContainer PVar = Nothing
emptyContainer PVar' = Nothing
emptyContainer (PConst c) = ()
emptyContainer (l `PProd` r) = (emptyContainer l, emptyContainer r)
emptyContainer (PLeft p) = emptyContainer p
emptyContainer (PRight p) = emptyContainer p
emptyContainer (PIn p) = emptyContainer p