1{-# LANGUAGE GeneralizedNewtypeDeriving, ConstraintKinds, PatternGuards, TupleSections #-}
2module Idris.Parser.Expr where
4import Prelude hiding (pi)
6import Text.Trifecta.Delta
7import Text.Trifecta hiding (span, stringLiteral, charLiteral, natural, symbol, char, string, whiteSpace, Err)
8import Text.Parser.LookAhead
9import Text.Parser.Expression
10import qualified Text.Parser.Token as Tok
11import qualified Text.Parser.Char as Chr
12import qualified Text.Parser.Token.Highlight as Hi
15import Idris.Parser.Helpers
16import Idris.Parser.Ops
21import Control.Applicative
23import Control.Monad.State.Strict
25import Data.Function (on)
27import qualified Data.List.Split as Spl
31import qualified Data.HashSet as HS
32import qualified Data.Text as T
33import qualified Data.ByteString.UTF8 as UTF8
37-- | Allow implicit type declarations
38allowImp :: SyntaxInfo -> SyntaxInfo
39allowImp syn = syn { implicitAllowed = True }
41-- | Disallow implicit type declarations
42disallowImp :: SyntaxInfo -> SyntaxInfo
43disallowImp syn = syn { implicitAllowed = False }
45{-| Parses an expression as a whole
47 FullExpr ::= Expr EOF_t;
50fullExpr :: SyntaxInfo -> IdrisParser PTerm
51fullExpr syn = do x <- expr syn
54 return $ debindApp syn (desugar syn i x)
56tryFullExpr :: SyntaxInfo -> IState -> String -> Either Err PTerm
57tryFullExpr syn st input =
58 case runparser (fullExpr syn) st "" input of
59 Success tm -> Right tm
60 Failure e -> Left (Msg (show e))
62{- | Parses an expression
67expr :: SyntaxInfo -> IdrisParser PTerm
70{- | Parses an expression with possible operator applied
72 OpExpr ::= {- Expression Parser with Operators based on Expr' -};
75opExpr :: SyntaxInfo -> IdrisParser PTerm
76opExpr syn = do i <- get
77 buildExpressionParser (table (idris_infixes i)) (expr' syn)
79{- | Parses either an internally defined expression or
82Expr' ::= "External (User-defined) Syntax"
86expr' :: SyntaxInfo -> IdrisParser PTerm
87expr' syn = try (externalExpr syn)
91{- | Parses a user-defined expression -}
92externalExpr :: SyntaxInfo -> IdrisParser PTerm
93externalExpr syn = do i <- get
94 (FC fn start _) <- getFC
95 expr <- extensions syn (syntaxRulesList $ syntax_rules i)
97 let outerFC = FC fn start end
98 return (mapPTermFC (fixFC outerFC) (fixFCH fn outerFC) expr)
99 <?> "user-defined expression"
100 where -- Fix non-highlighting FCs by approximating with the span of the syntax application
101 fixFC outer inner | inner `fcIn` outer = inner
103 -- Fix highlighting FCs by making them useless, to avoid spurious highlights
104 fixFCH fn outer inner | inner `fcIn` outer = inner
105 | otherwise = FileFC fn
107{- | Parses a simple user-defined expression -}
108simpleExternalExpr :: SyntaxInfo -> IdrisParser PTerm
109simpleExternalExpr syn = do i <- get
110 extensions syn (filter isSimple (syntaxRulesList $ syntax_rules i))
112 isSimple (Rule (Expr x:xs) _ _) = False
113 isSimple (Rule (SimpleExpr x:xs) _ _) = False
114 isSimple (Rule [Keyword _] _ _) = True
115 isSimple (Rule [Symbol _] _ _) = True
116 isSimple (Rule (_:xs) _ _) = case last xs of
122{- | Tries to parse a user-defined expression given a list of syntactic extensions -}
123extensions :: SyntaxInfo -> [Syntax] -> IdrisParser PTerm
124extensions syn rules = extension syn [] (filter isValid rules)
125 <?> "user-defined expression"
127 isValid :: Syntax -> Bool
128 isValid (Rule _ _ AnySyntax) = True
129 isValid (Rule _ _ PatternSyntax) = inPattern syn
130 isValid (Rule _ _ TermSyntax) = not (inPattern syn)
131 isValid (DeclRule _ _) = False
133data SynMatch = SynTm PTerm | SynBind FC Name -- ^ the FC is for highlighting information
136extension :: SyntaxInfo -> [Maybe (Name, SynMatch)] -> [Syntax] -> IdrisParser PTerm
137extension syn ns rules =
138 choice $ flip map (groupBy (ruleGroup `on` syntaxSymbols) rules) $ \rs ->
139 case head rs of -- can never be []
140 Rule (symb:_) _ _ -> try $ do
141 n <- extensionSymbol symb
142 extension syn (n : ns) [Rule ss t ctx | (Rule (_:ss) t ctx) <- rs]
143 -- If we have more than one Rule in this bucket, our grammar is
145 Rule [] ptm _ -> return (flatten (updateSynMatch (mapMaybe id ns) ptm))
147 ruleGroup [] [] = True
148 ruleGroup (s1:_) (s2:_) = s1 == s2
149 ruleGroup _ _ = False
151 extensionSymbol :: SSymbol -> IdrisParser (Maybe (Name, SynMatch))
152 extensionSymbol (Keyword n) = do fc <- reservedFC (show n)
153 highlightP fc AnnKeyword
155 extensionSymbol (Expr n) = do tm <- expr syn
156 return $ Just (n, SynTm tm)
157 extensionSymbol (SimpleExpr n) = do tm <- simpleExpr syn
158 return $ Just (n, SynTm tm)
159 extensionSymbol (Binding n) = do (b, fc) <- name
160 return $ Just (n, SynBind fc b)
161 extensionSymbol (Symbol s) = do fc <- symbolFC s
162 highlightP fc AnnKeyword
165 flatten :: PTerm -> PTerm -- flatten application
166 flatten (PApp fc (PApp _ f as) bs) = flatten (PApp fc f (as ++ bs))
169updateSynMatch = update
171 updateB :: [(Name, SynMatch)] -> (Name, FC) -> (Name, FC)
172 updateB ns (n, fc) = case lookup n ns of
173 Just (SynBind tfc t) -> (t, tfc)
176 update :: [(Name, SynMatch)] -> PTerm -> PTerm
177 update ns (PRef fc hls n) = case lookup n ns of
180 update ns (PPatvar fc n) = uncurry (flip PPatvar) $ updateB ns (n, fc)
181 update ns (PLam fc n nfc ty sc)
182 = let (n', nfc') = updateB ns (n, nfc)
183 in PLam fc n' nfc' (update ns ty) (update (dropn n ns) sc)
184 update ns (PPi p n fc ty sc)
185 = let (n', nfc') = updateB ns (n, fc)
186 in PPi (updTacImp ns p) n' nfc'
187 (update ns ty) (update (dropn n ns) sc)
188 update ns (PLet fc n nfc ty val sc)
189 = let (n', nfc') = updateB ns (n, nfc)
190 in PLet fc n' nfc' (update ns ty)
191 (update ns val) (update (dropn n ns) sc)
192 update ns (PApp fc t args)
193 = PApp fc (update ns t) (map (fmap (update ns)) args)
194 update ns (PAppBind fc t args)
195 = PAppBind fc (update ns t) (map (fmap (update ns)) args)
196 update ns (PMatchApp fc n) = let (n', nfc') = updateB ns (n, fc)
198 update ns (PIfThenElse fc c t f)
199 = PIfThenElse fc (update ns c) (update ns t) (update ns f)
200 update ns (PCase fc c opts)
201 = PCase fc (update ns c) (map (pmap (update ns)) opts)
202 update ns (PRewrite fc eq tm mty)
203 = PRewrite fc (update ns eq) (update ns tm) (fmap (update ns) mty)
204 update ns (PPair fc hls p l r) = PPair fc hls p (update ns l) (update ns r)
205 update ns (PDPair fc hls p l t r)
206 = PDPair fc hls p (update ns l) (update ns t) (update ns r)
207 update ns (PAs fc n t) = PAs fc (fst $ updateB ns (n, NoFC)) (update ns t)
208 update ns (PAlternative ms a as) = PAlternative ms a (map (update ns) as)
209 update ns (PHidden t) = PHidden (update ns t)
210 update ns (PGoal fc r n sc) = PGoal fc (update ns r) n (update ns sc)
211 update ns (PDoBlock ds) = PDoBlock $ map (upd ns) ds
212 where upd :: [(Name, SynMatch)] -> PDo -> PDo
213 upd ns (DoExp fc t) = DoExp fc (update ns t)
214 upd ns (DoBind fc n nfc t) = DoBind fc n nfc (update ns t)
215 upd ns (DoLet fc n nfc ty t) = DoLet fc n nfc (update ns ty) (update ns t)
216 upd ns (DoBindP fc i t ts)
217 = DoBindP fc (update ns i) (update ns t)
218 (map (\(l,r) -> (update ns l, update ns r)) ts)
219 upd ns (DoLetP fc i t) = DoLetP fc (update ns i) (update ns t)
220 update ns (PIdiom fc t) = PIdiom fc $ update ns t
221 update ns (PMetavar fc n) = uncurry (flip PMetavar) $ updateB ns (n, fc)
222 update ns (PProof tacs) = PProof $ map (updTactic ns) tacs
223 update ns (PTactics tacs) = PTactics $ map (updTactic ns) tacs
224 update ns (PDisamb nsps t) = PDisamb nsps $ update ns t
225 update ns (PUnifyLog t) = PUnifyLog $ update ns t
226 update ns (PNoImplicits t) = PNoImplicits $ update ns t
227 update ns (PQuasiquote tm mty) = PQuasiquote (update ns tm) (fmap (update ns) mty)
228 update ns (PUnquote t) = PUnquote $ update ns t
229 update ns (PQuoteName n res fc) = let (n', fc') = (updateB ns (n, fc))
230 in PQuoteName n' res fc'
231 update ns (PRunElab fc t nsp) = PRunElab fc (update ns t) nsp
232 update ns (PConstSugar fc t) = PConstSugar fc $ update ns t
233 -- PConstSugar probably can't contain anything substitutable, but it's hard to track
236 updTactic :: [(Name, SynMatch)] -> PTactic -> PTactic
237 -- handle all the ones with Names explicitly, then use fmap for the rest with PTerms
238 updTactic ns (Intro ns') = Intro $ map (fst . updateB ns . (, NoFC)) ns'
239 updTactic ns (Focus n) = Focus . fst $ updateB ns (n, NoFC)
240 updTactic ns (Refine n bs) = Refine (fst $ updateB ns (n, NoFC)) bs
241 updTactic ns (Claim n t) = Claim (fst $ updateB ns (n, NoFC)) (update ns t)
242 updTactic ns (MatchRefine n) = MatchRefine (fst $ updateB ns (n, NoFC))
243 updTactic ns (LetTac n t) = LetTac (fst $ updateB ns (n, NoFC)) (update ns t)
244 updTactic ns (LetTacTy n ty tm) = LetTacTy (fst $ updateB ns (n, NoFC)) (update ns ty) (update ns tm)
245 updTactic ns (ProofSearch rec prover depth top psns hints) = ProofSearch rec prover depth
246 (fmap (fst . updateB ns . (, NoFC)) top) (map (fst . updateB ns . (, NoFC)) psns) (map (fst . updateB ns . (, NoFC)) hints)
247 updTactic ns (Try l r) = Try (updTactic ns l) (updTactic ns r)
248 updTactic ns (TSeq l r) = TSeq (updTactic ns l) (updTactic ns r)
249 updTactic ns (GoalType s tac) = GoalType s $ updTactic ns tac
250 updTactic ns (TDocStr (Left n)) = TDocStr . Left . fst $ updateB ns (n, NoFC)
251 updTactic ns t = fmap (update ns) t
253 updTacImp ns (TacImp o st scr) = TacImp o st (update ns scr)
256 dropn :: Name -> [(Name, a)] -> [(Name, a)]
258 dropn n ((x,t) : xs) | n == x = xs
259 | otherwise = (x,t):dropn n xs
262{- | Parses a (normal) built-in expression
279internalExpr :: SyntaxInfo -> IdrisParser PTerm
296{- | Parses the "impossible" keyword
298Impossible ::= 'impossible'
301impossible :: IdrisParser PTerm
302impossible = do fc <- reservedFC "impossible"
303 highlightP fc AnnKeyword
306{- | Parses a case expression
309 'case' Expr 'of' OpenBlock CaseOption+ CloseBlock;
312caseExpr :: SyntaxInfo -> IdrisParser PTerm
313caseExpr syn = do kw1 <- reservedFC "case"; fc <- getFC
314 scr <- expr syn; kw2 <- reservedFC "of";
315 opts <- indentedBlock1 (caseOption syn)
316 highlightP kw1 AnnKeyword
317 highlightP kw2 AnnKeyword
318 return (PCase fc scr opts)
319 <?> "case expression"
321{- | Parses a case in a case expression
324 Expr (Impossible | '=>' Expr) Terminator
328caseOption :: SyntaxInfo -> IdrisParser (PTerm, PTerm)
329caseOption syn = do lhs <- expr (syn { inPattern = True })
330 r <- impossible <|> symbol "=>" *> expr syn
334warnTacticDeprecation :: FC -> IdrisParser ()
335warnTacticDeprecation fc =
336 parserWarning fc (Just NoOldTacticDeprecationWarnings) (Msg "This style of tactic proof is deprecated. See %runElab for the replacement.")
338{- | Parses a proof block
341 'proof' OpenBlock Tactic'* CloseBlock
345proofExpr :: SyntaxInfo -> IdrisParser PTerm
346proofExpr syn = do kw <- reservedFC "proof"
347 ts <- indentedBlock1 (tactic syn)
348 highlightP kw AnnKeyword
349 warnTacticDeprecation kw
353{- | Parses a tactics block
356 'tactics' OpenBlock Tactic'* CloseBlock
360tacticsExpr :: SyntaxInfo -> IdrisParser PTerm
361tacticsExpr syn = do kw <- reservedFC "tactics"
362 ts <- indentedBlock1 (tactic syn)
363 highlightP kw AnnKeyword
364 warnTacticDeprecation kw
368{- | Parses a simple expression
371 {- External (User-defined) Simple Expression -}
374 | 'Refl' ('{' Expr '}')?
392simpleExpr :: SyntaxInfo -> IdrisParser PTerm
394 try (simpleExternalExpr syn)
395 <|> do (x, FC f (l, c) end) <- try (lchar '?' *> name)
396 return (PMetavar (FC f (l, c-1) end) x)
397 <|> do lchar '%'; fc <- getFC; reserved "instance"; return (PResolveTC fc)
398 <|> do reserved "elim_for"; fc <- getFC; t <- fst <$> fnName; return (PRef fc [] (SN $ ElimN t))
401 <|> try (do reserved "Type*"; return $ PUniverse AllTypes)
402 <|> do reserved "AnyType"; return $ PUniverse AllTypes
403 <|> PType <$> reservedFC "Type"
404 <|> do reserved "UniqueType"; return $ PUniverse UniqueType
405 <|> do reserved "NullType"; return $ PUniverse NullType
406 <|> do (c, cfc) <- constant
408 return (modifyConst syn fc (PConstant cfc c))
409 <|> do symbol "'"; fc <- getFC; str <- fst <$> name
410 return (PApp fc (PRef fc [] (sUN "Symbol_"))
411 [pexp (PConstant NoFC (Str (show str)))])
412 <|> do (x, fc) <- fnName
414 then option (PRef fc [fc] x)
418 return (PAs fcIn x s))
419 else return (PRef fc [fc] x)
423 <|> do reservedOp "!"
426 return (PAppBind fc s [])
427 <|> bracketed (disallowImp syn)
431 <|> do lchar '_'; return Placeholder
434{- |Parses an expression in braces
436Bracketed ::= '(' Bracketed'
439bracketed :: SyntaxInfo -> IdrisParser PTerm
440bracketed syn = do (FC fn (sl, sc) _) <- getFC
441 lchar '(' <?> "parenthesized expression"
442 bracketed' (FC fn (sl, sc) (sl, sc+1)) syn
444{- |Parses the rest of an expression in braces
456bracketed' :: FC -> SyntaxInfo -> IdrisParser PTerm
458 do (FC f start (l, c)) <- getFC
460 return $ PTrue (spanFC open (FC f start (l, c+1))) TypeOrTerm
461 <|> try (dependentPair TypeOrTerm [] open syn)
462 <|> try (do fc <- getFC; o <- operator; e <- expr syn; lchar ')'
463 -- No prefix operators! (bit of a hack here...)
464 if (o == "-" || o == "!")
465 then fail "minus not allowed in section"
466 else return $ PLam fc (sMN 1000 "ARG") NoFC Placeholder
467 (PApp fc (PRef fc [] (sUN o)) [pexp (PRef fc [] (sMN 1000 "ARG")),
469 <|> try (do l <- simpleExpr syn
470 op <- option Nothing (do o <- operator
475 Nothing -> bracketedExpr syn open l
476 Just o -> return $ PLam fc0 (sMN 1000 "ARG") NoFC Placeholder
477 (PApp fc0 (PRef fc0 [] (sUN o)) [pexp l,
478 pexp (PRef fc0 [] (sMN 1000 "ARG"))]))
480 bracketedExpr syn open l
484{-| Parses the rest of a dependent pair after '(' or '(Expr **' -}
485dependentPair :: PunInfo -> [(PTerm, Maybe (FC, PTerm), FC)] -> FC -> SyntaxInfo -> IdrisParser PTerm
486dependentPair pun prev openFC syn =
488 nametypePart <|> namePart
491 IsType -> nametypePart <|> namePart <|> exprPart True
492 IsTerm -> exprPart False
493 TypeOrTerm -> nametypePart <|> namePart <|> exprPart False
494 where nametypePart = do
495 (ln, lnfc, colonFC) <- try $ do
497 colonFC <- lcharFC ':'
498 return (ln, lnfc, colonFC)
500 starsFC <- reservedOpFC "**"
501 dependentPair IsType ((PRef lnfc [] ln, Just (colonFC, lty), starsFC):prev) openFC syn
504 starsFC <- reservedOpFC "**"
505 dependentPair pun ((PRef lnfc [] ln, Nothing, starsFC):prev) openFC syn
509 let stars = (Left <$> reservedOpFC "**")
510 ending = (Right <$> lcharFC ')')
511 in if isEnd then ending else stars <|> ending
513 Left starsFC -> dependentPair IsTerm ((e, Nothing, starsFC):prev) openFC syn
515 return (mkPDPairs pun openFC closeFC (reverse prev) e)
516 mkPDPairs pun openFC closeFC ((e, cfclty, starsFC):bnds) r =
517 (PDPair openFC ([openFC] ++ maybe [] ((: []) . fst) cfclty ++ [starsFC, closeFC] ++ (=<<) (\(_,cfclty,sfc) -> maybe [] ((: []) . fst) cfclty ++ [sfc]) bnds)
518 pun e (maybe Placeholder snd cfclty) (mergePDPairs pun starsFC bnds r))
519 mergePDPairs pun starsFC' [] r = r
520 mergePDPairs pun starsFC' ((e, cfclty, starsFC):bnds) r =
521 PDPair starsFC' [] pun e (maybe Placeholder snd cfclty) (mergePDPairs pun starsFC bnds r)
523-- | Parse the contents of parentheses, after an expression has been parsed.
524bracketedExpr :: SyntaxInfo -> FC -> PTerm -> IdrisParser PTerm
525bracketedExpr syn openParenFC e =
526 do lchar ')'; return e
527 <|> do exprs <- some (do comma <- lcharFC ','
530 closeParenFC <- lcharFC ')'
531 let hilite = [openParenFC, closeParenFC] ++ map snd exprs
532 return $ PPair openParenFC hilite TypeOrTerm e (mergePairs exprs)
533 <|> do starsFC <- reservedOpFC "**"
534 dependentPair IsTerm [(e, Nothing, starsFC)] openParenFC syn
535 <?> "end of bracketed expression"
536 where mergePairs :: [(PTerm, FC)] -> PTerm
537 mergePairs [(t, fc)] = t
538 mergePairs ((t, fc):rs) = PPair fc [] TypeOrTerm t (mergePairs rs)
540-- bit of a hack here. If the integer doesn't fit in an Int, treat it as a
541-- big integer, otherwise try fromInteger and the constants as alternatives.
542-- a better solution would be to fix fromInteger to work with Integer, as the
543-- name suggests, rather than Int
544{-| Finds optimal type for integer constant -}
545modifyConst :: SyntaxInfo -> FC -> PTerm -> PTerm
546modifyConst syn fc (PConstant inFC (BI x))
547 | not (inPattern syn)
548 = PConstSugar inFC $ -- wrap in original location for highlighting
549 PAlternative [] FirstSuccess
550 (PApp fc (PRef fc [] (sUN "fromInteger")) [pexp (PConstant NoFC (BI (fromInteger x)))]
552 | otherwise = PConstSugar inFC $
553 PAlternative [] FirstSuccess consts
555 consts = [ PConstant inFC (BI x)
556 , PConstant inFC (I (fromInteger x))
557 , PConstant inFC (B8 (fromInteger x))
558 , PConstant inFC (B16 (fromInteger x))
559 , PConstant inFC (B32 (fromInteger x))
560 , PConstant inFC (B64 (fromInteger x))
562modifyConst syn fc x = x
564{- | Parses an alternative expression
566 Alt ::= '(|' Expr_List '|)';
570 | Expr' ',' Expr_List
574alt :: SyntaxInfo -> IdrisParser PTerm
575alt syn = do symbol "(|"; alts <- sepBy1 (expr' syn) (lchar ','); symbol "|)"
576 return (PAlternative [] FirstSuccess alts)
578{- | Parses a possibly hidden simple expression
586hsimpleExpr :: SyntaxInfo -> IdrisParser PTerm
594{- | Parses a unification log expression
596 '%' 'unifyLog' SimpleExpr
599unifyLog :: SyntaxInfo -> IdrisParser PTerm
600unifyLog syn = do (FC fn (sl, sc) kwEnd) <- try (lchar '%' *> reservedFC "unifyLog")
602 highlightP (FC fn (sl, sc-1) kwEnd) AnnKeyword
603 return (PUnifyLog tm)
604 <?> "unification log expression"
606{- | Parses a new-style tactics expression
608 '%' 'runElab' SimpleExpr
611runElab :: SyntaxInfo -> IdrisParser PTerm
612runElab syn = do (FC fn (sl, sc) kwEnd) <- try (lchar '%' *> reservedFC "runElab")
615 highlightP (FC fn (sl, sc-1) kwEnd) AnnKeyword
616 return $ PRunElab fc tm (syn_namespace syn)
617 <?> "new-style tactics expression"
619{- | Parses a disambiguation expression
624disamb :: SyntaxInfo -> IdrisParser PTerm
625disamb syn = do kw <- reservedFC "with"
626 ns <- sepBy1 (fst <$> name) (lchar ',')
628 highlightP kw AnnKeyword
629 return (PDisamb (map tons ns) tm)
630 <?> "namespace disambiguation expression"
631 where tons (NS n s) = txt (show n) : s
632 tons n = [txt (show n)]
633{- | Parses a no implicits expression
636 '%' 'noImplicits' SimpleExpr
640noImplicits :: SyntaxInfo -> IdrisParser PTerm
641noImplicits syn = do try (lchar '%' *> reserved "noImplicits")
643 return (PNoImplicits tm)
644 <?> "no implicits expression"
646{- | Parses a function application expression
654 SimpleExpr '<==' FnName
658app :: SyntaxInfo -> IdrisParser PTerm
659app syn = do f <- simpleExpr syn
660 (do try $ reservedOp "<=="
663 return (PLet fc (sMN 0 "match") NoFC
666 (PRef fc [] (sMN 0 "match")))
667 <?> "matching application expression") <|> (do
670 args <- many (do notEndApp; arg syn)
673 _ -> return (flattenFromInt fc f args))
674 <?> "function application"
676 -- bit of a hack to deal with the situation where we're applying a
677 -- literal to an argument, which we may want for obscure applications
678 -- of fromInteger, and this will help disambiguate better.
679 -- We know, at least, it won't be one of the constants!
680 flattenFromInt fc (PAlternative _ x alts) args
681 | Just i <- getFromInt alts
682 = PApp fc (PRef fc [] (sUN "fromInteger")) (i : args)
683 flattenFromInt fc f args = PApp fc f args
685 getFromInt ((PApp _ (PRef _ _ n) [a]) : _) | n == sUN "fromInteger" = Just a
686 getFromInt (_ : xs) = getFromInt xs
687 getFromInt _ = Nothing
689{-| Parses a function argument
698arg :: SyntaxInfo -> IdrisParser PArg
699arg syn = implicitArg syn
700 <|> constraintArg syn
701 <|> do e <- simpleExpr syn
703 <?> "function argument"
705{-| Parses an implicit function argument
708 '{' Name ('=' Expr)? '}'
712implicitArg :: SyntaxInfo -> IdrisParser PArg
713implicitArg syn = do lchar '{'
716 v <- option (PRef nfc [nfc] n) (do lchar '='
719 return (pimp n v True)
720 <?> "implicit function argument"
722{-| Parses a constraint argument (for selecting a named type class instance)
729constraintArg :: SyntaxInfo -> IdrisParser PArg
730constraintArg syn = do symbol "@{"
734 <?> "constraint argument"
736{-| Parses a quasiquote expression (for building reflected terms using the elaborator)
738> Quasiquote ::= '`(' Expr ')'
741quasiquote :: SyntaxInfo -> IdrisParser PTerm
742quasiquote syn = do startFC <- symbolFC "`("
743 e <- expr syn { syn_in_quasiquote = (syn_in_quasiquote syn) + 1 ,
746 do fc <- symbolFC ":"
747 ty <- expr syn { inPattern = False } -- don't allow antiquotes
749 endFC <- symbolFC ")"
750 mapM_ (uncurry highlightP) [(startFC, AnnKeyword), (endFC, AnnKeyword), (spanFC startFC endFC, AnnQuasiquote)]
752 Just (_, fc) -> highlightP fc AnnKeyword
754 return $ PQuasiquote e (fst <$> g)
757{-| Parses an unquoting inside a quasiquotation (for building reflected terms using the elaborator)
759> Unquote ::= ',' Expr
762unquote :: SyntaxInfo -> IdrisParser PTerm
763unquote syn = do guard (syn_in_quasiquote syn > 0)
764 startFC <- symbolFC "~"
765 e <- simpleExpr syn { syn_in_quasiquote = syn_in_quasiquote syn - 1 }
767 highlightP startFC AnnKeyword
768 highlightP (spanFC startFC endFC) AnnAntiquote
772{-| Parses a quotation of a name (for using the elaborator to resolve boring details)
774> NameQuote ::= '`{' Name '}'
777namequote :: SyntaxInfo -> IdrisParser PTerm
778namequote syn = do (startFC, res) <-
779 try (do fc <- symbolFC "`{{"
780 return (fc, False)) <|>
781 (do fc <- symbolFC "`{"
784 endFC <- if res then symbolFC "}" else symbolFC "}}"
785 mapM_ (uncurry highlightP)
786 [ (startFC, AnnKeyword)
787 , (endFC, AnnKeyword)
788 , (spanFC startFC endFC, AnnQuasiquote)
790 return $ PQuoteName n res nfc
794{-| Parses a record field setter expression
797 'record' '{' FieldTypeList '}';
802 | FieldType ',' FieldTypeList
811recordType :: SyntaxInfo -> IdrisParser PTerm
813 do kw <- reservedFC "record"
818 rec <- optional (simpleExpr syn)
819 highlightP kw AnnKeyword
824 return (PLam fc (sMN 0 "fldx") NoFC Placeholder
825 (applyAll fc fields (PRef fc [] (sMN 0 "fldx"))))
826 Just v -> return (applyAll fc fields v)
830 return (PLam fc (sMN 0 "fldx") NoFC Placeholder
831 (getAll fc (reverse fields)
832 (PRef fc [] (sMN 0 "fldx"))))
833 Just v -> return (getAll fc (reverse fields) v)
835 <?> "record setting expression"
836 where fieldSet :: IdrisParser ([Name], PTerm)
837 fieldSet = do ns <- fieldGet
843 fieldGet :: IdrisParser [Name]
844 fieldGet = sepBy1 (fst <$> fnName) (symbol "->")
846 fieldGetOrSet :: IdrisParser (Either [([Name], PTerm)] [Name])
847 fieldGetOrSet = try (do fs <- sepBy1 fieldSet (lchar ',')
852 applyAll :: FC -> [([Name], PTerm)] -> PTerm -> PTerm
854 applyAll fc ((ns, e) : es) x
855 = applyAll fc es (doUpdate fc ns e x)
857 doUpdate fc [n] e get
858 = PApp fc (PRef fc [] (mkType n)) [pexp e, pexp get]
859 doUpdate fc (n : ns) e get
860 = PApp fc (PRef fc [] (mkType n))
861 [pexp (doUpdate fc ns e (PApp fc (PRef fc [] n) [pexp get])),
864 getAll :: FC -> [Name] -> PTerm -> PTerm
865 getAll fc [n] e = PApp fc (PRef fc [] n) [pexp e]
866 getAll fc (n:ns) e = PApp fc (PRef fc [] n) [pexp (getAll fc ns e)]
869-- | Creates setters for record types on necessary functions
870mkType :: Name -> Name
871mkType (UN n) = sUN ("set_" ++ str n)
872mkType (MN 0 n) = sMN 0 ("set_" ++ str n)
873mkType (NS n s) = NS (mkType n) s
875{- | Parses a type signature
882TypeExpr ::= ConstraintList? Expr;
885typeExpr :: SyntaxInfo -> IdrisParser PTerm
886typeExpr syn = do cs <- if implicitAllowed syn then constraintList syn else return []
888 return (bindList (PPi constraint) cs sc)
891{- | Parses a lambda expression
894 '\\' TypeOptDeclList LambdaTail
895 | '\\' SimpleExprList LambdaTail
901 | SimpleExpr ',' SimpleExprList
910lambda :: SyntaxInfo -> IdrisParser PTerm
911lambda syn = do lchar '\\' <?> "lambda expression"
912 ((do xt <- try $ tyOptDeclList syn
915 return (bindList (PLam fc) xt sc))
917 (do ps <- sepBy (do fc <- getFC
918 e <- simpleExpr (syn { inPattern = True })
922 return (pmList (zip [0..] ps) sc)))
923 <?> "lambda expression"
924 where pmList :: [(Int, (FC, PTerm))] -> PTerm -> PTerm
926 pmList ((i, (fc, x)) : xs) sc
927 = PLam fc (sMN i "lamp") NoFC Placeholder
928 (PCase fc (PRef fc [] (sMN i "lamp"))
930 lambdaTail :: IdrisParser PTerm
931 lambdaTail = impossible <|> symbol "=>" *> expr syn
933{- | Parses a term rewrite expression
936 'rewrite' Expr ('==>' Expr)? 'in' Expr
940rewriteTerm :: SyntaxInfo -> IdrisParser PTerm
941rewriteTerm syn = do kw <- reservedFC "rewrite"
944 giving <- optional (do symbol "==>"; expr' syn)
945 kw' <- reservedFC "in"; sc <- expr syn
946 highlightP kw AnnKeyword
947 highlightP kw' AnnKeyword
949 (PApp fc (PRef fc [] (sUN "sym")) [pexp prf]) sc
951 <?> "term rewrite expression"
953{- |Parses a let binding
956 'let' Name TypeSig'? '=' Expr 'in' Expr
957| 'let' Expr' '=' Expr' 'in' Expr
964let_ :: SyntaxInfo -> IdrisParser PTerm
965let_ syn = try (do kw <- reservedFC "let"
966 ls <- indentedBlock (let_binding syn)
967 kw' <- reservedFC "in"; sc <- expr syn
968 highlightP kw AnnKeyword; highlightP kw' AnnKeyword
969 return (buildLets ls sc))
971 where buildLets [] sc = sc
972 buildLets ((fc, PRef nfc _ n, ty, v, []) : ls) sc
973 = PLet fc n nfc ty v (buildLets ls sc)
974 buildLets ((fc, pat, ty, v, alts) : ls) sc
975 = PCase fc v ((pat, buildLets ls sc) : alts)
977let_binding syn = do fc <- getFC;
978 pat <- expr' (syn { inPattern = True })
979 ty <- option Placeholder (do lchar ':'; expr' syn)
982 ts <- option [] (do lchar '|'
983 sepBy1 (do_alt syn) (lchar '|'))
984 return (fc,pat,ty,v,ts)
986{- | Parses a conditional expression
988If ::= 'if' Expr 'then' Expr 'else' Expr
992if_ :: SyntaxInfo -> IdrisParser PTerm
993if_ syn = (do ifFC <- reservedFC "if"
996 thenFC <- reservedFC "then"
998 elseFC <- reservedFC "else"
1000 mapM_ (flip highlightP AnnKeyword) [ifFC, thenFC, elseFC]
1001 return (PIfThenElse fc c t f))
1002 <?> "conditional expression"
1005{- | Parses a quote goal
1009 'quoteGoal' Name 'by' Expr 'in' Expr
1013quoteGoal :: SyntaxInfo -> IdrisParser PTerm
1014quoteGoal syn = do kw1 <- reservedFC "quoteGoal"; n <- fst <$> name;
1015 kw2 <- reservedFC "by"
1017 kw3 <- reservedFC "in"
1020 mapM_ (flip highlightP AnnKeyword) [kw1, kw2, kw3]
1021 return (PGoal fc r n sc)
1022 <?> "quote goal expression"
1024{- | Parses a dependent type signature
1027Pi ::= PiOpts Static? Pi'
1033 | '(' TypeDeclList ')' '->' Pi
1034 | '{' TypeDeclList '}' '->' Pi
1035 | '{' 'auto' TypeDeclList '}' '->' Pi
1036 | '{' 'default' SimpleExpr TypeDeclList '}' '->' Pi
1041bindsymbol opts st syn
1043 return (Exp opts st False)
1045explicitPi opts st syn
1046 = do xt <- try (lchar '(' *> typeDeclList syn <* lchar ')')
1047 binder <- bindsymbol opts st syn
1049 return (bindList (PPi binder) xt sc)
1051autoImplicit opts st syn
1052 = do kw <- reservedFC "auto"
1053 when (st == Static) $ fail "auto implicits can not be static"
1054 xt <- typeDeclList syn
1058 highlightP kw AnnKeyword
1059 return (bindList (PPi
1060 (TacImp [] Dynamic (PTactics [ProofSearch True True 100 Nothing [] []]))) xt sc)
1062defaultImplicit opts st syn = do
1063 kw <- reservedFC "default"
1064 when (st == Static) $ fail "default implicits can not be static"
1066 script' <- simpleExpr syn
1067 let script = debindApp syn . desugar syn ist $ script'
1068 xt <- typeDeclList syn
1072 highlightP kw AnnKeyword
1073 return (bindList (PPi (TacImp [] Dynamic script)) xt sc)
1075normalImplicit opts st syn = do
1076 xt <- typeDeclList syn <* lchar '}'
1078 cs <- constraintList syn
1081 = if implicitAllowed syn
1082 then (Imp opts st False (Just (Impl False True)) True,
1084 else (Imp opts st False (Just (Impl False False)) True,
1085 Imp opts st False (Just (Impl True False)) True)
1086 return (bindList (PPi im) xt
1087 (bindList (PPi cl) cs sc))
1089implicitPi opts st syn =
1090 autoImplicit opts st syn
1091 <|> defaultImplicit opts st syn
1092 <|> normalImplicit opts st syn
1094unboundPi opts st syn = do
1096 (do binder <- bindsymbol opts st syn
1098 return (PPi binder (sUN "__pi_arg") NoFC x sc))
1101pi :: SyntaxInfo -> IdrisParser PTerm
1103 do opts <- piOpts syn
1105 explicitPi opts st syn
1106 <|> try (do lchar '{'; implicitPi opts st syn)
1107 <|> unboundPi opts st syn
1108 <?> "dependent type signature"
1110{- | Parses Possible Options for Pi Expressions
1115piOpts :: SyntaxInfo -> IdrisParser [ArgOpt]
1116piOpts syn | implicitAllowed syn =
1117 lchar '.' *> return [InaccessibleArg]
1119piOpts syn = return []
1121{- | Parses a type constraint list
1125 '(' Expr_List ')' '=>'
1130constraintList :: SyntaxInfo -> IdrisParser [(Name, FC, PTerm)]
1131constraintList syn = try (constraintList1 syn)
1134constraintList1 :: SyntaxInfo -> IdrisParser [(Name, FC, PTerm)]
1135constraintList1 syn = try (do lchar '('
1136 tys <- sepBy1 nexpr (lchar ',')
1140 <|> try (do t <- opExpr (disallowImp syn)
1142 return [(defname, NoFC, t)])
1143 <?> "type constraint list"
1144 where nexpr = try (do (n, fc) <- name; lchar ':'
1147 <|> do e <- expr syn
1148 return (defname, NoFC, e)
1149 defname = sMN 0 "constraint"
1151{- | Parses a type declaration list
1154 FunctionSignatureList
1160FunctionSignatureList ::=
1162 | Name TypeSig ',' FunctionSignatureList
1166typeDeclList :: SyntaxInfo -> IdrisParser [(Name, FC, PTerm)]
1167typeDeclList syn = try (sepBy1 (do (x, xfc) <- fnName
1169 t <- typeExpr (disallowImp syn)
1172 <|> do ns <- sepBy1 name (lchar ',')
1174 t <- typeExpr (disallowImp syn)
1175 return (map (\(x, xfc) -> (x, xfc, t)) ns)
1176 <?> "type declaration list"
1178{- | Parses a type declaration list with optional parameters
1181 NameOrPlaceholder TypeSig?
1182 | NameOrPlaceholder TypeSig? ',' TypeOptDeclList
1187NameOrPlaceHolder ::= Name | '_';
1190tyOptDeclList :: SyntaxInfo -> IdrisParser [(Name, FC, PTerm)]
1191tyOptDeclList syn = sepBy1 (do (x, fc) <- nameOrPlaceholder
1192 t <- option Placeholder (do lchar ':'
1196 <?> "type declaration list"
1197 where nameOrPlaceholder :: IdrisParser (Name, FC)
1198 nameOrPlaceholder = fnName
1200 return (sMN 0 "underscore", NoFC)
1201 <?> "name or placeholder"
1203{- | Parses a list literal expression e.g. [1,2,3] or a comprehension [ (x, y) | x <- xs , y <- ys ]
1207 | '[' Expr '|' DoList ']'
1224listExpr :: SyntaxInfo -> IdrisParser PTerm
1225listExpr syn = do (FC f (l, c) _) <- getFC
1226 lchar '['; fc <- getFC;
1227 (try . token $ do (char ']' <?> "end of list expression")
1228 (FC _ _ (l', c')) <- getFC
1229 return (mkNil (FC f (l, c) (l', c'))))
1230 <|> (do x <- expr syn <?> "expression"
1231 (do try (lchar '|') <?> "list comprehension"
1232 qs <- sepBy1 (do_ syn) (lchar ',')
1234 return (PDoBlock (map addGuard qs ++
1235 [DoExp fc (PApp fc (PRef fc [] (sUN "return"))
1237 (do xs <- many (do (FC fn (sl, sc) _) <- getFC
1238 lchar ',' <?> "list element"
1239 let commaFC = FC fn (sl, sc) (sl, sc + 1)
1241 return (elt, commaFC))
1242 (FC fn (sl, sc) _) <- getFC
1243 lchar ']' <?> "end of list expression"
1244 let rbrackFC = FC fn (sl, sc) (sl, sc+1)
1245 return (mkList fc rbrackFC ((x, (FC f (l, c) (l, c+1))) : xs))))
1246 <?> "list expression"
1248 mkNil :: FC -> PTerm
1249 mkNil fc = PRef fc [fc] (sUN "Nil")
1250 mkList :: FC -> FC -> [(PTerm, FC)] -> PTerm
1251 mkList errFC nilFC [] = PRef nilFC [nilFC] (sUN "Nil")
1252 mkList errFC nilFC ((x, fc) : xs) = PApp errFC (PRef fc [fc] (sUN "::")) [pexp x, pexp (mkList errFC nilFC xs)]
1253 addGuard :: PDo -> PDo
1254 addGuard (DoExp fc e) = DoExp fc (PApp fc (PRef fc [] (sUN "guard"))
1258{- | Parses a do-block
1260Do' ::= Do KeepTerminator;
1265 'do' OpenBlock Do'+ CloseBlock
1269doBlock :: SyntaxInfo -> IdrisParser PTerm
1271 = do kw <- reservedFC "do"
1272 ds <- indentedBlock1 (do_ syn)
1273 highlightP kw AnnKeyword
1274 return (PDoBlock ds)
1277{- | Parses an expression inside a do block
1280 'let' Name TypeSig'? '=' Expr
1281 | 'let' Expr' '=' Expr
1288do_ :: SyntaxInfo -> IdrisParser PDo
1290 = try (do kw <- reservedFC "let"
1292 ty <- option Placeholder (do lchar ':'
1297 highlightP kw AnnKeyword
1298 return (DoLet fc i ifc ty e))
1299 <|> try (do kw <- reservedFC "let"
1304 highlightP kw AnnKeyword
1305 return (DoLetP fc i sc))
1306 <|> try (do (i, ifc) <- name
1310 option (DoBind fc i ifc e)
1312 ts <- sepBy1 (do_alt syn) (lchar '|')
1313 return (DoBindP fc (PRef ifc [ifc] i) e ts)))
1314 <|> try (do i <- expr' syn
1318 option (DoBindP fc i e [])
1320 ts <- sepBy1 (do_alt syn) (lchar '|')
1321 return (DoBindP fc i e ts)))
1322 <|> do e <- expr syn
1325 <?> "do block expression"
1327do_alt syn = do l <- expr' syn
1328 option (Placeholder, l)
1333{- | Parses an expression in idiom brackets
1335Idiom ::= '[|' Expr '|]';
1338idiom :: SyntaxInfo -> IdrisParser PTerm
1344 return (PIdiom fc e)
1345 <?> "expression in idiom brackets"
1347{- |Parses a constant or literal expression
1369constants :: [(String, Idris.Core.TT.Const)]
1371 [ ("Integer", AType (ATInt ITBig))
1372 , ("Int", AType (ATInt ITNative))
1373 , ("Char", AType (ATInt ITChar))
1374 , ("Double", AType ATFloat)
1375 , ("String", StrType)
1376 , ("prim__WorldType", WorldType)
1377 , ("prim__TheWorld", TheWorld)
1378 , ("Bits8", AType (ATInt (ITFixed IT8)))
1379 , ("Bits16", AType (ATInt (ITFixed IT16)))
1380 , ("Bits32", AType (ATInt (ITFixed IT32)))
1381 , ("Bits64", AType (ATInt (ITFixed IT64)))
1384-- | Parse a constant and its source span
1385constant :: IdrisParser (Idris.Core.TT.Const, FC)
1386constant = choice [ do fc <- reservedFC name; return (ty, fc)
1387 | (name, ty) <- constants
1389 <|> do (f, fc) <- try float; return (Fl f, fc)
1390 <|> do (i, fc) <- natural; return (BI i, fc)
1391 <|> do (s, fc) <- verbatimStringLiteral; return (Str s, fc)
1392 <|> do (s, fc) <- stringLiteral; return (Str s, fc)
1393 <|> do (c, fc) <- try charLiteral; return (Ch c, fc) --Currently ambigous with symbols
1394 <?> "constant or literal"
1396{- | Parses a verbatim multi-line string literal (triple-quoted)
1400 '\"\"\"' ~'\"\"\"' '\"\"\"'
1404verbatimStringLiteral :: MonadicParsing m => m (String, FC)
1405verbatimStringLiteral = token $ do (FC f start _) <- getFC
1406 try $ string "\"\"\""
1407 str <- manyTill anyChar $ try (string "\"\"\"")
1408 (FC _ _ end) <- getFC
1409 return (str, FC f start end)
1411{- | Parses a static modifier
1419static :: IdrisParser Static
1420static = do reserved "%static"; return Static
1421 <|> do reserved "[static]"
1423 parserWarning fc Nothing (Msg "The use of [static] is deprecated, use %static instead.")
1426 <?> "static modifier"
1428{- | Parses a tactic script
1431Tactic ::= 'intro' NameList?
1433 | 'refine' Name Imp+
1438 | 'let' Name ':' Expr' '=' Expr
1439 | 'let' Name '=' Expr
1442 | 'applyTactic' Expr
1445 | 'try' Tactic '|' Tactic
1463 | Tactic ';' TacticSeq
1468-- | A specification of the arguments that tactics can take
1469data TacticArg = NameTArg -- ^ Names: n1, n2, n3, ... n
1474-- The FIXMEs are Issue #1766 in the issue tracker.
1475-- https://github.com/idris-lang/Idris-dev/issues/1766
1476-- | A list of available tactics and their argument requirements
1477tactics :: [([String], Maybe TacticArg, SyntaxInfo -> IdrisParser PTactic)]
1479 [ (["intro"], Nothing, const $ -- FIXME syntax for intro (fresh name)
1480 do ns <- sepBy (spaced (fst <$> name)) (lchar ','); return $ Intro ns)
1481 , noArgs ["intros"] Intros
1482 , noArgs ["unfocus"] Unfocus
1483 , (["refine"], Just ExprTArg, const $
1484 do n <- spaced (fst <$> fnName)
1486 return $ Refine n imps)
1487 , (["claim"], Nothing, \syn ->
1488 do n <- indentPropHolds gtProp *> (fst <$> name)
1489 goal <- indentPropHolds gtProp *> expr syn
1490 return $ Claim n goal)
1491 , (["mrefine"], Just ExprTArg, const $
1492 do n <- spaced (fst <$> fnName)
1493 return $ MatchRefine n)
1494 , expressionTactic ["rewrite"] Rewrite
1495 , expressionTactic ["case"] CaseTac
1496 , expressionTactic ["induction"] Induction
1497 , expressionTactic ["equiv"] Equiv
1498 , (["let"], Nothing, \syn -> -- FIXME syntax for let
1499 do n <- (indentPropHolds gtProp *> (fst <$> name))
1500 (do indentPropHolds gtProp *> lchar ':'
1501 ty <- indentPropHolds gtProp *> expr' syn
1502 indentPropHolds gtProp *> lchar '='
1503 t <- indentPropHolds gtProp *> expr syn
1505 return $ LetTacTy n (desugar syn i ty) (desugar syn i t))
1506 <|> (do indentPropHolds gtProp *> lchar '='
1507 t <- indentPropHolds gtProp *> expr syn
1509 return $ LetTac n (desugar syn i t)))
1511 , (["focus"], Just ExprTArg, const $
1512 do n <- spaced (fst <$> name)
1514 , expressionTactic ["exact"] Exact
1515 , expressionTactic ["applyTactic"] ApplyTactic
1516 , expressionTactic ["byReflection"] ByReflection
1517 , expressionTactic ["reflect"] Reflect
1518 , expressionTactic ["fill"] Fill
1519 , (["try"], Just AltsTArg, \syn ->
1520 do t <- spaced (tactic syn)
1522 t1 <- spaced (tactic syn)
1524 , noArgs ["compute"] Compute
1525 , noArgs ["trivial"] Trivial
1526 , noArgs ["unify"] DoUnify
1527 , (["search"], Nothing, const $
1528 do depth <- option 10 $ fst <$> natural
1529 return (ProofSearch True True (fromInteger depth) Nothing [] []))
1530 , noArgs ["instance"] TCInstance
1531 , noArgs ["solve"] Solve
1532 , noArgs ["attack"] Attack
1533 , noArgs ["state", ":state"] ProofState
1534 , noArgs ["term", ":term"] ProofTerm
1535 , noArgs ["undo", ":undo"] Undo
1536 , noArgs ["qed", ":qed"] Qed
1537 , noArgs ["abandon", ":q"] Abandon
1538 , noArgs ["skip"] Skip
1539 , noArgs ["sourceLocation"] SourceFC
1540 , expressionTactic [":e", ":eval"] TEval
1541 , expressionTactic [":t", ":type"] TCheck
1542 , expressionTactic [":search"] TSearch
1543 , (["fail"], Just StringLitTArg, const $
1544 do msg <- fst <$> stringLiteral
1545 return $ TFail [Idris.Core.TT.TextPart msg])
1546 , ([":doc"], Just ExprTArg, const $
1548 doc <- (Right . fst <$> constant) <|> (Left . fst <$> fnName)
1550 return (TDocStr doc))
1553 expressionTactic names tactic = (names, Just ExprTArg, \syn ->
1554 do t <- spaced (expr syn)
1556 return $ tactic (desugar syn i t))
1557 noArgs names tactic = (names, Nothing, const (return tactic))
1558 spaced parser = indentPropHolds gtProp *> parser
1559 imp :: IdrisParser Bool
1560 imp = do lchar '?'; return False
1561 <|> do lchar '_'; return True
1564tactic :: SyntaxInfo -> IdrisParser PTactic
1565tactic syn = choice [ do choice (map reserved names); parser syn
1566 | (names, _, parser) <- tactics ]
1570 ts <- sepBy1 (tactic syn) (lchar ';')
1572 return $ TSeq t (mergeSeq ts)
1573 <|> ((lchar ':' >> empty) <?> "prover command")
1576 mergeSeq :: [PTactic] -> PTactic
1578 mergeSeq (t:ts) = TSeq t (mergeSeq ts)
1580-- | Parses a tactic as a whole
1581fullTactic :: SyntaxInfo -> IdrisParser PTactic
1582fullTactic syn = do t <- tactic syn
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