---------------------------------------------------------------------------- -- | -- Module : CSPM.Interpreter.PatternMatcher -- Copyright : (c) Fontaine 2008 -- License : BSD -- -- Maintainer : Fontaine@cs.uni-duesseldorf.de -- Stability : experimental -- Portability : GHC-only -- -- Execute the selectors of a compilied pattern with a Value. -- ---------------------------------------------------------------------------- {- todo : Compiling selectors to pattern meight be an over-kill. maybe its simpler and faster to implement direct pattern-matching -} {-# LANGUAGE BangPatterns, ViewPatterns #-} module CSPM.Interpreter.PatternMatcher ( match ,tryMatchStrict ,tryMatchLazy ,boundNames ) where import Language.CSPM.AST as AST hiding (Bindings) import CSPM.Interpreter.Types as Types import CSPM.Interpreter.Bindings import Data.Maybe import qualified Data.Set as Set import Control.Exception import Data.Array.IArray as Array import qualified Data.List as List failedMatch :: Maybe Value failedMatch = Nothing typeError :: String -> Value -> Maybe Value typeError x v = throwTypingError ("error in pattern-match : "++ x) Nothing $ Just v -- todo make match strict !BangPattern match :: Value -> Selector -> Maybe Value match (VInt a) (IntSel b) = if a==b then return VUnit else failedMatch match v (IntSel _) = typeError "expecting Int" v match (VBool True) TrueSel = return VUnit match (VBool False) TrueSel = failedMatch match v TrueSel = typeError "expecting Bool" v match (VBool True) FalseSel = failedMatch match (VBool False) FalseSel = return VUnit match v FalseSel = typeError "expecting Bool" v match x SelectThis = return x match (VChannel ch) (ConstrSel ident) = if AST.uniqueIdentId ident == chanId ch then return VUnit else failedMatch match (VConstructor (Types.Constructor i _ _)) (ConstrSel ident) = if AST.uniqueIdentId ident == i then return VUnit else failedMatch match v (ConstrSel c) = typeError ("expecting constructor " ++ show c) v -- | DotSel Int Int Selector match (VSet _) (SingleSetSel _) = throwFeatureNotImplemented "single set pattern" Nothing match v (SingleSetSel _) = typeError "expecting a set" v match (VSet s) EmptySetSel = if Set.null s then return VUnit else failedMatch match v EmptySetSel = typeError "expecting a set" v -- todo : really test this match (VList l) p = case p of ListIthSel i next -> match (l !! i) next ListLengthSel 0 _next -> if null l then return VUnit else failedMatch ListLengthSel len next -> if length l == len then matchList len l next else failedMatch _ -> matchList (length l) l p match t@(VTuple b) (TupleLengthSel len next) = if length b == len then match t next else typeError "tuple wrong arity" t match v (TupleLengthSel _ n) = typeError "expecting tuple" v match (VTuple b) (TupleIthSel i next) = match (b !! i) next match v (TupleIthSel _ n) = typeError "expecting tuple" v match (VDotTuple l) (DotSel i next) = match (l !! i) next match v (DotSel _ _) = typeError "expecting dot-tuple" v match v p = throwInternalError ("hit catchall case of pattern-matcher :" ++ show (v,p)) Nothing $ Just v matchList :: Int -> [Value] -> Selector -> Maybe Value matchList s !l !sel = case sel of SelectThis -> return $ VList l HeadSel next -> if null l then failedMatch else match (head l) next HeadNSel len next -> if s >= len then matchList len (take len l) next else failedMatch PrefixSel offset len next -> if s >= offset + len then matchList len (take len $ drop offset l) next else failedMatch TailSel next -> if not $ null l then matchList (s-1) (tail l) next else failedMatch SliceSel offsetL offsetR next -> if s >= offsetL + offsetR then let newLen = s - offsetL -offsetR in matchList newLen (take newLen $ drop offsetL l) next else failedMatch SuffixSel offset len next -> if s >= offset + len then let offsetLeft = s - offset - len in matchList len (take len $ drop offsetLeft l) next else failedMatch ListLengthSel len next -> if s == len then matchList s l next else failedMatch ListIthSel i next -> match (l !! i) next other -> throwTypingError ("matchList : not excpecting a List :" ++ show other) Nothing (Just $ VList l) {- If we force the result we first force the value we match against and then we check all selectors ! We must be careful about lazyness/strictness here ! todo: maybe use ST-Transformer to fold over the array / do some optimisations avoid detour via lists -} -- | tryMatchStrict returns Nothing or a new Binding tryMatchStrict :: Bindings -> LPattern -> Value -> Maybe Bindings tryMatchStrict !binds p !val = case unLabel p of VarPat ident -> Just $ bindIdent ident val binds Selector sel ident -> case match val sel of Nothing -> Nothing Just valPart -> case ident of Nothing -> Just binds Just i -> Just $ bindIdent i valPart binds Selectors selectorL identArray -> do values <- matchGroup val selectorL let addBind b i = case identArray Array.! i of Just n -> bindIdent n (values Array.! i) b Nothing -> b return $ List.foldl' addBind binds $ Array.indices identArray _ -> throwInternalError "PatternMatcher : unsupported Pattern in strict match" (Just $ srcLoc p) Nothing -- | tryMatchLazy allways return a new Binding, but may throw a error when -- | any value in the binding is forced -- | forcing one of the values causes all the selectors being tested {- todo : Fix THISBUG: If we have Selectors which all do not bind a new Ident, still should to force the value , so that we can detect a failing match -} tryMatchLazy :: Bindings -> LPattern -> Value -> Bindings tryMatchLazy binds p@(unLabel -> VarPat ident) val = bindIdent ident val binds tryMatchLazy binds p@(unLabel -> Selector sel ident) val = case ident of Just i -> bindIdent i valPart binds Nothing -> binds -- THISBUG where valPart = case match val sel of Just v -> v Nothing -> throwPatternMatchError "pattern-match failure" (Just $ srcLoc p) $ Just val tryMatchLazy binds sel@(unLabel -> Selectors selectorss identArray) val = List.foldl' addBind binds $ Array.indices identArray where values = case matchGroup val selectorss of Just x -> x Nothing -> throwPatternMatchError "pattern-match failure" (Just $ srcLoc sel) $ Just val addBind b i = case identArray Array.! i of Just n -> bindIdent n (values Array.! i) b Nothing -> b -- THISBUG tryMatchLazy _ p v = throwInternalError "PatternMatcher : unsupported Pattern in lazyMatch" (Just $ srcLoc p) $ Just v {- If we force one of the values, we also have to force all of the corresponding linear selectors !! todo : for efficiency specialize this for small selectors -} matchGroup :: Value -> Array Int Selector -> Maybe (Array Int Value) matchGroup val sel = do l <- mapM (match val) $ Array.elems sel return $ Array.listArray (Array.bounds sel) l boundNames :: LPattern -> [LIdent] boundNames pat = case unLabel pat of VarPat i -> [i] Selector _ Nothing -> [] Selector _ (Just i) -> [i] x@(Selectors {}) -> catMaybes $ Array.elems $ idents x