1{-# LANGUAGE PatternGuards #-}
3module Idris.CaseSplit(splitOnLine, replaceSplits,
4 getClause, getProofClause,
6 getUniq, nameRoot) where
8-- splitting a variable in a pattern clause
13import Idris.Delaborate
24import Control.Monad.State.Strict
26import Text.Parser.Combinators
27import Text.Parser.Char(anyChar)
28import Text.Trifecta(Result(..), parseString)
29import Text.Trifecta.Delta
30import qualified Data.ByteString.UTF8 as UTF8
36Given a pattern clause and a variable 'n', elaborate the clause and find the
39Make new pattern clauses by replacing 'n' with all the possibly constructors
40applied to '_', and replacing all other variables with '_' in order to
41resolve other dependencies.
43Finally, merge the generated patterns with the original, by matching.
44Always take the "more specific" argument when there is a discrepancy, i.e.
45names over '_', patterns over names, etc.
48-- Given a variable to split, and a term application, return a list of
50split :: Name -> PTerm -> Idris [[(Name, PTerm)]]
53 (tm, ty, pats) <- elabValBind toplevel True (addImplPat ist t')
54 logLvl 4 ("Elaborated:\n" ++ show tm ++ " : " ++ show ty ++ "\n" ++ show pats)
55-- iputStrLn (show (delab ist tm) ++ " : " ++ show (delab ist ty))
56-- iputStrLn (show pats)
57 let t = mergeUserImpl (addImplPat ist t') (delab ist tm)
58 let ctxt = tt_ctxt ist
60 Nothing -> fail $ show n ++ " is not a pattern variable"
62 do let splits = findPats ist ty
63 iLOG ("New patterns " ++ showSep ", "
64 (map (showImp Nothing True False) splits))
65 let newPats_in = zipWith (replaceVar ctxt n) splits (repeat t)
66 logLvl 4 ("Working from " ++ show t)
67 logLvl 4 ("Trying " ++ showSep "\n"
68 (map (showImp Nothing True False) newPats_in))
69 newPats <- mapM elabNewPat newPats_in
70 logLvl 3 ("Original:\n" ++ show t)
71 logLvl 3 ("Split:\n" ++
72 (showSep "\n" (map show (mapMaybe id newPats))))
74 let newPats' = mergeAllPats ctxt t (mapMaybe id newPats)
75 iLOG ("Name updates " ++ showSep "\n"
76 (map (\ (p, u) -> show u ++ " " ++ show p) newPats'))
77 return (map snd newPats')
79data MergeState = MS { namemap :: [(Name, Name)],
80 invented :: [(Name, Name)],
82 updates :: [(Name, PTerm)] }
84addUpdate n tm = do ms <- get
85 put (ms { updates = ((n, stripNS tm) : updates ms) } )
89 let badnames = map snd (namemap ms) ++ map snd (invented ms) ++
91 case lookup n (invented ms) of
96 uniqueName (UN "var") badnames
97 MN i n -> uniqueName (UN n) badnames
99 put (ms { invented = (n, n') : invented ms })
102stripNS tm = mapPT dens tm where
103 dens (PRef fc n) = PRef fc (nsroot n)
106mergeAllPats :: Context -> PTerm -> [PTerm] -> [(PTerm, [(Name, PTerm)])]
107mergeAllPats ctxt t [] = []
108mergeAllPats ctxt t (p : ps)
109 = let (p', MS _ _ _ u) = runState (mergePat ctxt t p)
110 (MS [] [] (patvars t) [])
111 ps' = mergeAllPats ctxt t ps in
113 where patvars (PRef _ n) = [n]
114 patvars (PApp _ _ as) = concatMap (patvars . getTm) as
115 patvars (PPatvar _ n) = [n]
118mergePat :: Context -> PTerm -> PTerm -> State MergeState PTerm
119-- If any names are unified, make sure they stay unified. Always prefer
120-- user provided name (first pattern)
121mergePat ctxt (PPatvar fc n) new
122 = mergePat ctxt (PRef fc n) new
123mergePat ctxt old (PPatvar fc n)
124 = mergePat ctxt old (PRef fc n)
125mergePat ctxt orig@(PRef fc n) new@(PRef _ n')
126 | isDConName n' ctxt = do addUpdate n new;
130 case lookup n' (namemap ms) of
131 Just x -> do addUpdate n (PRef fc x)
133 Nothing -> do put (ms { namemap = ((n', n) : namemap ms) })
135mergePat ctxt (PApp _ _ args) (PApp fc f args')
136 = do newArgs <- zipWithM mergeArg args args'
137 return (PApp fc f newArgs)
138 where mergeArg x y = do tm' <- mergePat ctxt (getTm x) (getTm y)
140 (PImp _ _ _ _ _ _) ->
141 return (y { machine_inf = machine_inf x,
143 _ -> return (y { getTm = tm' })
144mergePat ctxt (PRef fc n) t = do tm <- tidy t
147mergePat ctxt x y = return y
149mergeUserImpl :: PTerm -> PTerm -> PTerm
154 case lookup n (namemap ms) of
155 Just x -> return (PRef fc x)
157 (UN _) -> return orig
158 _ -> do n' <- inventName n
161 = do args' <- mapM tidyArg args
162 return (PApp fc f args')
163 where tidyArg x = do tm' <- tidy (getTm x)
164 return (x { getTm = tm' })
169-- where tidyVar (PRef _ _) = Placeholder
172elabNewPat :: PTerm -> Idris (Maybe PTerm)
173elabNewPat t = idrisCatch (do (tm, ty) <- elabVal toplevel True t
175 return (Just (delab i tm)))
176 (\e -> do i <- getIState
177 logLvl 5 $ "Not a valid split:\n" ++ pshow i e
180findPats :: IState -> Type -> [PTerm]
181findPats ist t | (P _ n _, _) <- unApply t
182 = case lookupCtxt n (idris_datatypes ist) of
183 [ti] -> map genPat (con_names ti)
185 where genPat n = case lookupCtxt n (idris_implicits ist) of
186 [args] -> PApp emptyFC (PRef emptyFC n)
187 (map toPlaceholder args)
188 _ -> error $ "Can't happen (genPat) " ++ show n
189 toPlaceholder tm = tm { getTm = Placeholder }
190findPats ist t = [Placeholder]
192replaceVar :: Context -> Name -> PTerm -> PTerm -> PTerm
193replaceVar ctxt n t (PApp fc f pats) = PApp fc f (map substArg pats)
194 where subst :: PTerm -> PTerm
195 subst orig@(PPatvar _ v) | v == n = t
196 | otherwise = Placeholder
197 subst orig@(PRef _ v) | v == n = t
198 | isDConName v ctxt = orig
199 subst (PRef _ _) = Placeholder
200 subst (PApp fc (PRef _ t) pats)
201 | isTConName t ctxt = Placeholder -- infer types
202 subst (PApp fc f pats) = PApp fc f (map substArg pats)
203 subst (PEq fc l r) = PEq fc (subst l) (subst r)
206 substArg arg = arg { getTm = subst (getTm arg) }
208replaceVar ctxt n t pat = pat
210splitOnLine :: Int -- ^ line number
211 -> Name -- ^ variable
212 -> FilePath -- ^ name of file
213 -> Idris [[(Name, PTerm)]]
214splitOnLine l n fn = do
215-- let (before, later) = splitAt (l-1) (lines inp)
217 cl <- getInternalApp fn l
218 logLvl 3 ("Working with " ++ showImp Nothing True False cl)
220-- iputStrLn (showSep "\n" (map show tms))
221 return tms -- "" -- not yet done...
223replaceSplits :: String -> [[(Name, PTerm)]] -> Idris [String]
224replaceSplits l ups = updateRHSs 1 (map (rep (expandBraces l)) ups)
226 rep str [] = str ++ "\n"
227 rep str ((n, tm) : ups) = rep (updatePat False (show n) (nshow False tm) str) ups
229 updateRHSs i [] = return []
230 updateRHSs i (x : xs) = do (x', i') <- updateRHS i x
231 xs' <- updateRHSs i' xs
234 updateRHS i ('?':'=':xs) = do (xs', i') <- updateRHS i xs
235 return ("?=" ++ xs', i')
236 updateRHS i ('?':xs) = do let (nm, rest) = span (not . isSpace) xs
237 (nm', i') <- getUniq nm i
238 return ('?':nm' ++ rest, i')
239 updateRHS i (x : xs) = do (xs', i') <- updateRHS i xs
241 updateRHS i [] = return ("", i)
244 -- TMP HACK: If there are Nats, we don't want to show as numerals since
245 -- this isn't supported in a pattern, so special case here
246 nshow brack (PRef _ (UN "Z")) = "Z"
247 nshow brack (PApp _ (PRef _ (UN "S")) [x]) =
248 if brack then "(S " else "S " ++ nshow True (getTm x) ++
249 if brack then ")" else ""
252 -- if there's any {n} replace with {n=n}
253 expandBraces ('{' : xs)
254 = let (brace, (_:rest)) = span (/= '}') xs in
255 if (not ('=' `elem` brace))
256 then ('{' : brace ++ " = " ++ brace ++ "}") ++
258 else ('{' : brace ++ "}") ++ expandBraces rest
259 expandBraces (x : xs) = x : expandBraces xs
262 updatePat start n tm [] = []
263 updatePat start n tm ('{':rest) =
264 let (space, rest') = span isSpace rest in
265 '{' : space ++ updatePat False n tm rest'
266 updatePat True n tm xs@(c:rest) | length xs > length n
267 = let (before, after@(next:_)) = splitAt (length n) xs in
268 if (before == n && not (isAlpha next))
269 then addBrackets tm ++ updatePat False n tm after
270 else c : updatePat (not (isAlpha c)) n tm rest
271 updatePat start n tm (c:rest) = c : updatePat (not (isAlpha c)) n tm rest
273 addBrackets tm | ' ' `elem` tm = "(" ++ tm ++ ")"
277 = do ist <- getIState
278 let n = nameRoot [] nm ++ "_" ++ show i
279 case lookupTy (UN n) (tt_ctxt ist) of
280 [] -> return (n, i+1)
281 _ -> getUniq nm (i+1)
283nameRoot acc nm | all isDigit nm = showSep "_" acc
285 case span (/='_') nm of
286 (before, ('_' : after)) -> nameRoot (acc ++ [before]) after
287 _ -> showSep "_" (acc ++ [nm])
289getClause :: Int -- ^ line number that the type is declared on
290 -> Name -- ^ Function name
291 -> FilePath -- ^ Source file name
293getClause l fn fp = do ty <- getInternalApp fp l
294 let ap = mkApp ty [1..]
295 return (show fn ++ " " ++ ap ++
296 "= ?" ++ show fn ++ "_rhs")
297 where mkApp (PPi (Exp _ _ _ False) (MN _ _) _ sc) (n : ns)
298 = "x" ++ show n ++ " " ++ mkApp sc ns
299 mkApp (PPi (Exp _ _ _ False) n _ sc) ns
300 = show n ++ " " ++ mkApp sc ns
301 mkApp (PPi _ _ _ sc) ns = mkApp sc ns
304getProofClause :: Int -- ^ line number that the type is declared
305 -> Name -- ^ Function name
306 -> FilePath -- ^ Source file name
308getProofClause l fn fp
309 = do ty <- getInternalApp fp l
310 return (mkApp ty ++ " = ?" ++ show fn ++ "_rhs")
311 where mkApp (PPi _ _ _ sc) = mkApp sc
312 mkApp rt = "(" ++ show rt ++ ") <== " ++ show fn
314-- Purely syntactic - turn a pattern match clause into a with and a new
317mkWith :: String -> Name -> String
318mkWith str n = let str' = replaceRHS str "with (_)"
320 replaceRHS str "| with_pat = ?" ++ show n ++ "_rhs" in
321 str' ++ "\n" ++ newpat
323 where replaceRHS [] str = str
324 replaceRHS ('?':'=': rest) str = str
325 replaceRHS ('=': rest) str
326 | not ('=' `elem` rest) = str
327 replaceRHS (x : rest) str = x : replaceRHS rest str
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list