module SimpPatMatch(module SimpPatMatch,HasOrig,HasIdTy,HasSrcLoc,ValueId) where import Maybe(isJust,fromMaybe) import Substitute import Recursive import HasBaseStruct import BaseSyntax hiding (Var) import DefinedNames(definedVars) import TiNames(ValueId,localVal') --import TiPrelude(prelError,prelEqual,prelGE,prelMinus) import TiClasses(HasDef,noDef,nullDef) import TypedIds import UniqueNames(orig,HasOrig) import FreeNames --import DefinedNames import PrettyPrint(pp) import MUtils(( # ),( <# ),apFst) simpAllPatMatch ids m = mapExp (simpPatMatch ids) m data SimpPatIds i = Ids {prelError,prelEqual,prelGE,prelMinus::HsIdentI i} getSimpPatIds pv = Ids # pv "error" <# pv "==" <# pv ">=" <# pv "-" class SimpPatMatch i s | s->i where simpPatMatch :: SimpPatIds i -> s -> s instance SimpPatMatch i s => SimpPatMatch i [s] where simpPatMatch = map . simpPatMatch simpPatMatchE ids e = simpPatMatchE' ids rec e simpPatMatchE' ids rec e0 = case mapEI id (simpPatMatch ids) id (simpAllPatMatch ids) id id e0 of HsCase e alts -> convMatch ids (compilePatMatch ids e alts) e -> rec e
Pattern match simplification, as described in section 3.17.3 of the Haskell 98 report.
{- compilePatMatch :: (ValueId i,HasSrcLoc i,HasIdTy n i,HasOrig i,HasOrig n, HasBaseStruct e (EI i e p ds t c),GetBaseStruct e (EI i e p ds t c), FreeNames i e,--DefinedNames i p, HasBaseStruct p (PI i p),GetBaseStruct p (PI i p), HasBaseStruct d (DI i e p ds t c tp), HasDef ds d) => SimpPatIds i -> e -> [HsAlt e p ds] -> Match i e p --} compilePatMatch ids = patMatch_a where Ids prelError prelEqual prelGE prelMinus = ids -- Rule (a) patMatch_a e alts = mustBeVar' "v0" (srcLoc alts) (Match e) (patMatch_b alts) -- Rule (b) patMatch_b alts v = foldr (patMatch_c v) err alts where err = nomatch s "No match" s = srcLoc alts nomatch s msg = NoMatch s msg -- Rule (c) patMatch_c v (HsAlt s p rhs ds) e' = case rhs of HsBody e -> patMatch_c' v s p e ds e' HsGuard gdrhss -> mustBeVar s e' rhs where rhs y = patMatch_c' v s p (ifGuards (hsEVar y) gdrhss) ds (Var y) ifGuards = foldr ifGuard ifGuard (s,g,e) = hsIf g e where -- Rule (c), after the guards have been handled patMatch_c' v s p e ds e' = patMatch_defghij v s p (hsLet' ds e) e' -- Introduce a variable to avoid code duplication, if necessary: mustBeVarM s e cont = mustBeVar s e (cont. Var) --mustBeVarE s e cont = mustBeVar s e (cont. hsEVar) mustBeVar s = mustBeVar' "y0" s mustBeVar' n s e cont = case isEVar =<< isMatch e of Just y -> cont y _ -> monoLet y e (cont y) where y = localVal' n (Just s) -- Rule (d)-(j) patMatch_defghij v s p rhs e' = case basestruct p of Just bp -> case bp of HsPIrrPat p' -> patMatch_d v s p' rhs -- rule (d) HsPAsPat x p' -> patMatch_defghij v s p' (monoLetVar x v rhs) e' -- rule (e) HsPWildCard -> rhs -- rule (f) HsPApp k ps@(_:_) -> patMatch_g (srcLoc k) v (hsPApp k) ps rhs e' -- rule (g) HsPTuple s ps -> patMatch_g s v (hsPTuple s) ps rhs e' -- rule (g) HsPInfixApp p1 k p2 -> patMatch_g (srcLoc k) v k' [p1,p2] rhs e' -- rule (g) where k' [p1,p2] = hsPInfixApp p1 k p2 HsPLit s lit -> -- rule (h) If (hsEVar v `eqTest` hsLit s lit) rhs e' HsPNeg s lit -> If (hsEVar v `eqTest` hsNegApp s (hsLit s lit)) rhs e' HsPSucc s n k -> -- rule (s), horror!! If (ve `geTest` ke) (monoLet n (Match (ve `minus` ke)) rhs) e' where ve = hsEVar v ; ke = hsLit s k HsPId (HsVar x) -> monoLetVar x v rhs -- rules (i) and (j) HsPParen p' -> patMatch_defghij v s p' rhs e' HsPList s ps -> patMatch_g s v (hsPList s) ps rhs e' -- rule (g), hmm!! HsPRec k lps -> patMatch_mno v k lps rhs e' _ -> keep _ -> keep where keep = patMatch_keep v s p rhs e' eqTest = binop prelEqual geTest = binop prelGE minus = binop prelMinus binop op e1 e2 = hsId op `hsApp` e1 `hsApp` e2 -- Rule (d) patMatch_d v s p' rhs = monoLets xs (map proj xs) rhs where xs = map getHSName (definedVars p') proj x = patMatch_defghij v s p' (Match (hsEVar x)) (nomatch s "Irrefutable pattern failed") -- Rule (g) patMatch_g s v k ps rhs e' = mustBeVarM s e' (patMatch_g' s v k ps rhs) patMatch_g' s v k ps rhs e' = simpleCase v s p' body e' where p' = k (map hsPVar xs) xs = zipWith reuse ps (freshnames s 'q' ps) body = foldr submatch rhs (zip xs ps) submatch (x,p) rest = patMatch_defghij x s p rest e' reuse p x = fromMaybe x (isPVar p) -- Rules (m)-(o), labelled fields patMatch_mno v k lps rhs e' = case lps of [] -> patMatch_o v k rhs e' [HsField f p] -> patMatch_n v k f p rhs e' HsField f p:lps' -> -- rule (m) mustBeVarM (srcLoc k) e' $ \ ey -> patMatch_n v k f p (patMatch_mno v k lps' rhs ey) ey -- Rule (n) patMatch_n v k f p rhs e' = simpleCase v (srcLoc k) p' rhs e' where p' = hsPApp k (map posPat fs) posPat f' = if orig f'==orig f then p else hsPWildCard fs = confields k -- Rule (o) patMatch_o v k rhs e' = simpleCase v (srcLoc k) p' rhs e' where p' = hsPApp k (replicate n hsPWildCard) n = conarity k patMatch_keep v = simpleCase v -- remaining cases and unimplemented rules isMatch (Match e) = Just e isMatch _ = Nothing
A data types to represent the output from the pattern match simplififier. This makes further transformation easier.
data Match i e p = NoMatch SrcLoc String | Match e | Var i -- special case for Match (hsEVar x) | MonoLets [i] [Match i e p] (Match i e p) | SimpleCase SrcLoc i p (Match i e p) (Match i e p) | If e (Match i e p) (Match i e p) convMatch :: (HasBaseStruct p (PI i p),GetBaseStruct p (PI i p),HasDef ds d,HasIdTy n i, FreeNames i e,HasBaseStruct e (EI i e p ds t c)) => SimpPatIds i -> Match i e p -> e convMatch ids = conv . simp where Ids prelError prelEqual prelGE prelMinus = ids conv m = case m of NoMatch s msg -> nomatch s msg Match rhs -> rhs Var x -> hsEVar x MonoLets xs es m' -> monoLets xs (map conv es) (conv m') SimpleCase s v p rhs def -> simpleCase v s p rhs (flatten v def) If e m1 m2 -> hsIf e (conv m1) (conv m2) nomatch s msg = hsId prelError `hsApp` hsLit s (HsString (pp s++": "++msg)) monoLets [] [] e' = e' monoLets xs0 es0 e' = hsParen (hsLambda' [hsPVar x|x<-xs] e') `apps` es where apps e es = foldl hsApp e es -- Omit bindings for variables that aren't used: (xs,es) = unzip . filter (keep.fst) $ zip xs0 es0 keep x = x `elem` fvs_e' fvs_e' = fvs e' flatten v (SimpleCase s v' p rhs def) | v'==v = apFst ((s,p,rhs):) (flatten v def) flatten v m = ([],m) simpleCase v s p rhs ([],NoMatch{}) | irrefutable p = hsCase (hsEVar v) [alt (s,p,rhs)] simpleCase v s p rhs (alts,def) = hsCase (hsEVar v) (map alt ((s,p,rhs):alts)++default_alt) where default_alt = [alt (s,hsPWildCard,def)] alt (s,p,rhs) = HsAlt s p (HsBody (conv rhs)) noDef simp m = case m of MonoLets xs es m' -> case simpLet xs (map simp es) (simp m') of ([], [] ,m') -> m' (xs',es',m') -> MonoLets xs' es' m' SimpleCase s v p rhs def -> SimpleCase s v p (simp rhs) def' where def' = if irrefutable p then NoMatch s "unreachable" else simp def If e m1 m2 -> If e (simp m1) (simp m2) _ -> m simpLet xs es m = foldr simp1 ([],[],m) (zip xs es) where simp1 (x,e) (xs,es,m) = case count x m of 0 -> (xs,es,m) --1 -> (xs,es,subst e x m) -- causes code explosion in hs2alfa _ -> (x:xs,e:es,m) -- Does x occur zero, one or many times in m? count x m = case m of NoMatch s msg -> 0 Match rhs -> if x `elem` fvs rhs then 2 else 0::Int Var y -> if y==x then 1 else 0 MonoLets xs es m' -> sum (map (count x) es)+count x m' -- x not in xs SimpleCase s v p rhs def -> if v==x then 2 else count x rhs+count x def If e m1 m2 -> if x `elem` fvs e then 2 else count x m1+count x m2 {- -- pre: x occurs as Var x in one place subst e x m = case m of NoMatch s msg -> m Match rhs -> m Var y -> if y==x then e else m MonoLets xs es m' -> MonoLets xs (map su es) (su m') SimpleCase s v p rhs def -> SimpleCase s v p (su rhs) (su def) If e m1 m2 -> If e (su m1) (su m2) where su = subst e x -} fvs e = [v|(HsVar v,ValueNames)<-freeNames e] hsLambda' [] e = e hsLambda' xs e = hsLambda xs e simpleCase v s p rhs e' = SimpleCase s v p rhs e' monoLetVar x y = if x==y then id else monoLet x (Match (hsEVar y)) -- Monomorphic, nonrecursive variants of let x = e in e' -- (The Haskell report uses two versions) --monoLet s x e e' = hsCase e [HsAlt s (hsPVar x) (HsBody e') noDef] monoLet x e = MonoLets [x] [e] monoLets [] [] e' = e' monoLets xs es e' = MonoLets xs es e' hsLet' ds e = Match (if nullDef ds then e else hsLet ds e) irrefutable p = isJust (isPVar p) || isJust (isIrrPat p) || isWildCardPat p || case isAsPat p of Just (_,p) -> irrefutable p _ -> False || case basestruct p of Just (HsPTuple _ ps) -> all irrefutable ps Just (HsPInfixApp p1 k p2) -> concount k==1 && all irrefutable [p1,p2] Just (HsPApp k ps) -> concount k==1 && all irrefutable ps _ -> False conarity k= conArity . conInfo $ k confields k = conFields' . conInfo $ k conFields' = fromMaybe [] . conFields conInfo k = head [con|con<-constrs k,orig (conName con)==orig k] concount k = length (constrs k) constrs k = case idTy k of ConstrOf _ ti -> constructors ti freshnames s x ps = [localVal' (x:show n) (Just s)|n<-[1..length ps]]