2Module : Idris.REPL.Parser
3Description : Parser for the REPL commands.
5Maintainer : The Idris Community.
7module Idris.REPL.Parser (
20import qualified Idris.Parser as IP
21import qualified Idris.Parser.Expr as IP
22import qualified Idris.Parser.Helpers as IP
23import qualified Idris.Parser.Ops as IP
25import Idris.REPL.Commands
27import Control.Applicative
28import Control.Monad.State.Strict
29import Data.Char (isSpace, toLower)
31import Data.List.Split (splitOn)
32import System.Console.ANSI (Color(..))
33import System.FilePath ((</>))
34import Text.Parser.Char (anyChar, oneOf)
35import Text.Parser.Combinators
37parseCmd :: IState -> String -> String -> Either IP.ParseError (Either String Command)
38parseCmd i inputname = IP.runparser pCmd i inputname . trim
40 where f = reverse . dropWhile isSpace
42type CommandTable = [ ( [String], CmdArg, String
43 , String -> IP.IdrisParser (Either String Command) ) ]
45setOptions :: [(String, Opt)]
46setOptions = [("errorcontext", ErrContext),
47 ("showimplicits", ShowImpl),
48 ("originalerrors", ShowOrigErr),
49 ("autosolve", AutoSolve),
50 ("nobanner", NoBanner),
51 ("warnreach", WarnReach),
52 ("evaltypes", EvalTypes),
53 ("desugarnats", DesugarNats)]
55help :: [([String], CmdArg, String)]
56help = (["<expr>"], NoArg, "Evaluate an expression") :
57 [ (map (':' :) names, args, text) | (names, args, text, _) <- parserCommandsForHelp ]
59allHelp :: [([String], CmdArg, String)]
60allHelp = [ (map (':' :) names, args, text)
61 | (names, args, text, _) <- parserCommandsForHelp ++ parserCommands ]
63parserCommandsForHelp :: CommandTable
64parserCommandsForHelp =
65 [ exprArgCmd ["t", "type"] Check "Check the type of an expression"
66 , exprArgCmd ["core"] Core "View the core language representation of a term"
67 , nameArgCmd ["miss", "missing"] Missing "Show missing clauses"
68 , (["doc"], NameArg, "Show internal documentation", cmd_doc)
69 , (["mkdoc"], NamespaceArg, "Generate IdrisDoc for namespace(s) and dependencies"
70 , genArg "namespace" (many anyChar) MakeDoc)
71 , (["apropos"], SeqArgs (OptionalArg PkgArgs) NameArg, " Search names, types, and documentation"
73 , (["s", "search"], SeqArgs (OptionalArg PkgArgs) ExprArg
74 , " Search for values by type", cmd_search)
75 , nameArgCmd ["wc", "whocalls"] WhoCalls "List the callers of some name"
76 , nameArgCmd ["cw", "callswho"] CallsWho "List the callees of some name"
77 , namespaceArgCmd ["browse"] Browse "List the contents of some namespace"
78 , nameArgCmd ["total"] TotCheck "Check the totality of a name"
79 , noArgCmd ["r", "reload"] Reload "Reload current file"
80 , noArgCmd ["w", "watch"] Watch "Watch the current file for changes"
81 , (["l", "load"], FileArg, "Load a new file"
82 , strArg (\f -> Load f Nothing))
83 , (["!"], ShellCommandArg, "Run a shell command", strArg RunShellCommand)
84 , (["cd"], FileArg, "Change working directory"
85 , strArg ChangeDirectory)
86 , (["module"], ModuleArg, "Import an extra module", moduleArg ModImport) -- NOTE: dragons
87 , noArgCmd ["e", "edit"] Edit "Edit current file using $EDITOR or $VISUAL"
88 , noArgCmd ["m", "metavars"] Metavars "Show remaining proof obligations (metavariables or holes)"
89 , (["p", "prove"], MetaVarArg, "Prove a metavariable"
90 , nameArg (Prove False))
91 , (["elab"], MetaVarArg, "Build a metavariable using the elaboration shell"
92 , nameArg (Prove True))
93 , (["a", "addproof"], NameArg, "Add proof to source file", cmd_addproof)
94 , (["rmproof"], NameArg, "Remove proof from proof stack"
96 , (["showproof"], NameArg, "Show proof"
98 , noArgCmd ["proofs"] Proofs "Show available proofs"
99 , exprArgCmd ["x"] ExecVal "Execute IO actions resulting from an expression using the interpreter"
100 , (["c", "compile"], FileArg, "Compile to an executable [codegen] <filename>", cmd_compile)
101 , (["exec", "execute"], OptionalArg ExprArg, "Compile to an executable and run", cmd_execute)
102 , (["dynamic"], FileArg, "Dynamically load a C library (similar to %dynamic)", cmd_dynamic)
103 , (["dynamic"], NoArg, "List dynamically loaded C libraries", cmd_dynamic)
104 , noArgCmd ["?", "h", "help"] Help "Display this help text"
105 , optArgCmd ["set"] SetOpt $ "Set an option (" ++ optionsList ++ ")"
106 , optArgCmd ["unset"] UnsetOpt "Unset an option"
107 , (["color", "colour"], ColourArg
108 , "Turn REPL colours on or off; set a specific colour"
110 , (["consolewidth"], ConsoleWidthArg, "Set the width of the console", cmd_consolewidth)
111 , (["printerdepth"], OptionalArg NumberArg, "Set the maximum pretty-printer depth (no arg for infinite)", cmd_printdepth)
112 , noArgCmd ["q", "quit"] Quit "Exit the Idris system"
113 , noArgCmd ["warranty"] Warranty "Displays warranty information"
114 , (["let"], ManyArgs DeclArg
115 , "Evaluate a declaration, such as a function definition, instance implementation, or fixity declaration"
117 , (["unlet", "undefine"], ManyArgs NameArg
118 , "Remove the listed repl definitions, or all repl definitions if no names given"
120 , nameArgCmd ["printdef"] PrintDef "Show the definition of a function"
121 , (["pp", "pprint"], (SeqArgs OptionArg (SeqArgs NumberArg NameArg))
122 , "Pretty prints an Idris function in either LaTeX or HTML and for a specified width."
124 , (["verbosity"], NumberArg, "Set verbosity level", cmd_verb)
126 where optionsList = intercalate ", " $ map fst setOptions
129parserCommands :: CommandTable
131 [ noArgCmd ["u", "universes"] Universes "Display universe constraints"
132 , noArgCmd ["errorhandlers"] ListErrorHandlers "List registered error handlers"
134 , nameArgCmd ["d", "def"] Defn "Display a name's internal definitions"
135 , nameArgCmd ["transinfo"] TransformInfo "Show relevant transformation rules for a name"
136 , nameArgCmd ["di", "dbginfo"] DebugInfo "Show debugging information for a name"
138 , exprArgCmd ["patt"] Pattelab "(Debugging) Elaborate pattern expression"
139 , exprArgCmd ["spec"] Spec "?"
140 , exprArgCmd ["whnf"] WHNF "(Debugging) Show weak head normal form of an expression"
141 , exprArgCmd ["inline"] TestInline "?"
142 , proofArgCmd ["cs", "casesplit"] CaseSplitAt
143 ":cs <line> <name> splits the pattern variable on the line"
144 , proofArgCmd ["apc", "addproofclause"] AddProofClauseFrom
145 ":apc <line> <name> adds a pattern-matching proof clause to name on line"
146 , proofArgCmd ["ac", "addclause"] AddClauseFrom
147 ":ac <line> <name> adds a clause for the definition of the name on the line"
148 , proofArgCmd ["am", "addmissing"] AddMissing
149 ":am <line> <name> adds all missing pattern matches for the name on the line"
150 , proofArgCmd ["mw", "makewith"] MakeWith
151 ":mw <line> <name> adds a with clause for the definition of the name on the line"
152 , proofArgCmd ["mc", "makecase"] MakeCase
153 ":mc <line> <name> adds a case block for the definition of the metavariable on the line"
154 , proofArgCmd ["ml", "makelemma"] MakeLemma "?"
155 , (["log"], NumberArg, "Set logging level", cmd_log)
158 , "Set logging categories"
160 , (["lto", "loadto"], SeqArgs NumberArg FileArg
161 , "Load file up to line number", cmd_loadto)
162 , (["ps", "proofsearch"], NoArg
163 , ":ps <line> <name> <names> does proof search for name on line, with names as hints"
165 , (["ref", "refine"], NoArg
166 , ":ref <line> <name> <name'> attempts to partially solve name on line, with name' as hint, introducing metavariables for arguments that aren't inferrable"
168 , (["debugunify"], SeqArgs ExprArg ExprArg
169 , "(Debugging) Try to unify two expressions", const $ do
170 l <- IP.simpleExpr defaultSyntax
171 r <- IP.simpleExpr defaultSyntax
173 return (Right (DebugUnify l r))
177noArgCmd names command doc =
178 (names, NoArg, doc, noArgs command)
179nameArgCmd names command doc =
180 (names, NameArg, doc, fnNameArg command)
181namespaceArgCmd names command doc =
182 (names, NamespaceArg, doc, namespaceArg command)
183exprArgCmd names command doc =
184 (names, ExprArg, doc, exprArg command)
185optArgCmd names command doc =
186 (names, OptionArg, doc, optArg command)
187proofArgCmd names command doc =
188 (names, NoArg, doc, proofArg command)
190pCmd :: IP.IdrisParser (Either String Command)
191pCmd = choice [ do c <- cmd names; parser c
192 | (names, _, _, parser) <- parserCommandsForHelp ++ parserCommands ]
196 where nop = do eof; return (Right NOP)
200 let cmd' = takeWhile (/=' ') cmd
201 return (Left $ "Unrecognized command: " ++ cmd')
203cmd :: [String] -> IP.IdrisParser String
208 where docmd [] = fail "Could not parse command"
209 docmd (x:xs) = try (IP.reserved x >> return x) <|> docmd xs
211 sorted_xs = sortBy (\x y -> compare (length y) (length x)) xs
214noArgs :: Command -> String -> IP.IdrisParser (Either String Command)
220 let failure = return (Left $ ":" ++ name ++ " takes no arguments")
222 emptyArgs <|> failure
224eval :: IP.IdrisParser (Either String Command)
226 t <- IP.fullExpr defaultSyntax
227 return $ Right (Eval t)
229exprArg :: (PTerm -> Command) -> String -> IP.IdrisParser (Either String Command)
233 return $ Left ("Usage is :" ++ name ++ " <expression>")
235 let justOperator = do
236 (op, fc) <- IP.symbolicOperatorFC
238 return $ Right $ cmd (PRef fc [] (sUN op))
241 t <- IP.fullExpr defaultSyntax
242 return $ Right (cmd t)
243 try noArg <|> try justOperator <|> properArg
245genArg :: String -> IP.IdrisParser a -> (a -> Command)
246 -> String -> IP.IdrisParser (Either String Command)
247genArg argName argParser cmd name = do
248 let emptyArgs = do eof; failure
249 oneArg = do arg <- argParser
251 return (Right (cmd arg))
252 try emptyArgs <|> oneArg <|> failure
254 failure = return $ Left ("Usage is :" ++ name ++ " <" ++ argName ++ ">")
256nameArg, fnNameArg :: (Name -> Command) -> String -> IP.IdrisParser (Either String Command)
257nameArg = genArg "name" $ fst <$> IP.name
258fnNameArg = genArg "functionname" $ fst <$> IP.fnName
260strArg :: (String -> Command) -> String -> IP.IdrisParser (Either String Command)
261strArg = genArg "string" (many anyChar)
263moduleArg :: (FilePath -> Command) -> String -> IP.IdrisParser (Either String Command)
264moduleArg = genArg "module" (fmap (toPath . fst) IP.identifier)
266 toPath n = foldl1' (</>) $ splitOn "." n
268namespaceArg :: ([String] -> Command) -> String -> IP.IdrisParser (Either String Command)
269namespaceArg = genArg "namespace" (fmap (toNS . fst) IP.identifier)
273optArg :: (Opt -> Command) -> String -> IP.IdrisParser (Either String Command)
277 return $ Left ("Usage is :" ++ name ++ " <option>")
283 return (Right (cmd o))
285 let failure = return $ Left "Unrecognized setting"
287 try emptyArgs <|> oneArg <|> failure
290 pOption :: IP.IdrisParser Opt
291 pOption = foldl (<|>) empty $ map (\(a, b) -> do discard (IP.symbol a); return b) setOptions
293proofArg :: (Bool -> Int -> Name -> Command) -> String -> IP.IdrisParser (Either String Command)
294proofArg cmd name = do
295 upd <- option False $ do
298 l <- fst <$> IP.natural
299 n <- fst <$> IP.name;
300 return (Right (cmd upd (fromInteger l) n))
302cmd_doc :: String -> IP.IdrisParser (Either String Command)
305 c <- fmap fst IP.constant
307 return $ Right (DocStr (Right c) FullDocs)
312 return $ Right (DocStr (Left $ IP.mkName ("Type", "")) FullDocs)
314 let fnName = fnNameArg (\n -> DocStr (Left n) FullDocs) name
316 try constant <|> pType <|> fnName
318cmd_consolewidth :: String -> IP.IdrisParser (Either String Command)
319cmd_consolewidth name = do
321 return (Right (SetConsoleWidth w))
324 pConsoleWidth :: IP.IdrisParser ConsoleWidth
325 pConsoleWidth = do discard (IP.symbol "auto"); return AutomaticWidth
326 <|> do discard (IP.symbol "infinite"); return InfinitelyWide
327 <|> do n <- fmap (fromInteger . fst) IP.natural
330cmd_printdepth :: String -> IP.IdrisParser (Either String Command)
331cmd_printdepth _ = do d <- optional (fmap (fromInteger . fst) IP.natural)
332 return (Right $ SetPrinterDepth d)
334cmd_execute :: String -> IP.IdrisParser (Either String Command)
336 tm <- option maintm (IP.fullExpr defaultSyntax)
337 return (Right (Execute tm))
339 maintm = PRef (fileFC "(repl)") [] (sNS (sUN "main") ["Main"])
341cmd_dynamic :: String -> IP.IdrisParser (Either String Command)
343 let optArg = do l <- many anyChar
345 then return $ Right (DynamicLink l)
346 else return $ Right ListDynamic
347 let failure = return $ Left $ "Usage is :" ++ name ++ " [<library>]"
348 try optArg <|> failure
350cmd_pprint :: String -> IP.IdrisParser (Either String Command)
354 n <- fmap (fromInteger . fst) IP.natural
356 t <- IP.fullExpr defaultSyntax
357 return (Right (PPrint fmt n t))
360 ppFormat :: IP.IdrisParser OutputFmt
361 ppFormat = (discard (IP.symbol "html") >> return HTMLOutput)
362 <|> (discard (IP.symbol "latex") >> return LaTeXOutput)
364cmd_compile :: String -> IP.IdrisParser (Either String Command)
366 let defaultCodegen = Via IBCFormat "c"
368 let codegenOption :: IP.IdrisParser Codegen
370 let bytecodeCodegen = discard (IP.symbol "bytecode") *> return Bytecode
371 viaCodegen = do x <- fst <$> IP.identifier
372 return (Via IBCFormat (map toLower x))
373 bytecodeCodegen <|> viaCodegen
377 f <- fst <$> IP.identifier
379 return $ Right (Compile defaultCodegen f)
383 codegen <- codegenOption
384 f <- fst <$> IP.identifier
386 return $ Right (Compile codegen f)
388 let failure = return $ Left $ "Usage is :" ++ name ++ " [<codegen>] <filename>"
389 try hasTwoArgs <|> try hasOneArg <|> failure
391cmd_addproof :: String -> IP.IdrisParser (Either String Command)
392cmd_addproof name = do
393 n <- option Nothing $ do
397 return (Right (AddProof n))
399cmd_log :: String -> IP.IdrisParser (Either String Command)
401 i <- fmap (fromIntegral . fst) IP.natural
403 return (Right (LogLvl i))
405cmd_verb :: String -> IP.IdrisParser (Either String Command)
407 i <- fmap (fromIntegral . fst) IP.natural
409 return (Right (Verbosity i))
411cmd_cats :: String -> IP.IdrisParser (Either String Command)
413 cs <- sepBy pLogCats (IP.whiteSpace)
415 return $ Right $ LogCategory (concat cs)
418 c <- fst <$> IP.identifier
419 fail $ "Category: " ++ c ++ " is not recognised."
421 pLogCats :: IP.IdrisParser [LogCat]
422 pLogCats = try (parserCats <$ IP.symbol (strLogCat IParse))
423 <|> try (elabCats <$ IP.symbol (strLogCat IElab))
424 <|> try (codegenCats <$ IP.symbol (strLogCat ICodeGen))
425 <|> try ([ICoverage] <$ IP.symbol (strLogCat ICoverage))
426 <|> try ([IIBC] <$ IP.symbol (strLogCat IIBC))
427 <|> try ([IErasure] <$ IP.symbol (strLogCat IErasure))
430cmd_let :: String -> IP.IdrisParser (Either String Command)
432 defn <- concat <$> many (IP.decl defaultSyntax)
433 return (Right (NewDefn defn))
435cmd_unlet :: String -> IP.IdrisParser (Either String Command)
436cmd_unlet name = (Right . Undefine) `fmap` many (fst <$> IP.name)
438cmd_loadto :: String -> IP.IdrisParser (Either String Command)
440 toline <- fmap (fromInteger . fst) IP.natural
442 return (Right (Load f (Just toline)))
444cmd_colour :: String -> IP.IdrisParser (Either String Command)
445cmd_colour name = fmap Right pSetColourCmd
448 colours :: [(String, Maybe Color)]
449 colours = [ ("black", Just Black)
451 , ("green", Just Green)
452 , ("yellow", Just Yellow)
453 , ("blue", Just Blue)
454 , ("magenta", Just Magenta)
455 , ("cyan", Just Cyan)
456 , ("white", Just White)
457 , ("default", Nothing)
460 pSetColourCmd :: IP.IdrisParser Command
461 pSetColourCmd = (do c <- pColourType
462 let defaultColour = IdrisColour Nothing True False False False
463 opts <- sepBy pColourMod (IP.whiteSpace)
464 let colour = foldr ($) defaultColour $ reverse opts
465 return $ SetColour c colour)
466 <|> try (IP.symbol "on" >> return ColourOn)
467 <|> try (IP.symbol "off" >> return ColourOff)
469 pColour :: IP.IdrisParser (Maybe Color)
470 pColour = doColour colours
471 where doColour [] = fail "Unknown colour"
472 doColour ((s, c):cs) = (try (IP.symbol s) >> return c) <|> doColour cs
474 pColourMod :: IP.IdrisParser (IdrisColour -> IdrisColour)
475 pColourMod = try (doVivid <$ IP.symbol "vivid")
476 <|> try (doDull <$ IP.symbol "dull")
477 <|> try (doUnderline <$ IP.symbol "underline")
478 <|> try (doNoUnderline <$ IP.symbol "nounderline")
479 <|> try (doBold <$ IP.symbol "bold")
480 <|> try (doNoBold <$ IP.symbol "nobold")
481 <|> try (doItalic <$ IP.symbol "italic")
482 <|> try (doNoItalic <$ IP.symbol "noitalic")
483 <|> try (pColour >>= return . doSetColour)
484 where doVivid i = i { vivid = True }
485 doDull i = i { vivid = False }
486 doUnderline i = i { underline = True }
487 doNoUnderline i = i { underline = False }
488 doBold i = i { bold = True }
489 doNoBold i = i { bold = False }
490 doItalic i = i { italic = True }
491 doNoItalic i = i { italic = False }
492 doSetColour c i = i { colour = c }
494 -- | Generate the colour type names using the default Show instance.
495 colourTypes :: [(String, ColourType)]
496 colourTypes = map (\x -> ((map toLower . reverse . drop 6 . reverse . show) x, x)) $
497 enumFromTo minBound maxBound
499 pColourType :: IP.IdrisParser ColourType
500 pColourType = doColourType colourTypes
501 where doColourType [] = fail $ "Unknown colour category. Options: " ++
502 (concat . intersperse ", " . map fst) colourTypes
503 doColourType ((s,ct):cts) = (try (IP.symbol s) >> return ct) <|> doColourType cts
505idChar = oneOf (['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ ['_'])
507cmd_apropos :: String -> IP.IdrisParser (Either String Command)
508cmd_apropos = packageBasedCmd (some idChar) Apropos
510packageBasedCmd :: IP.IdrisParser a -> ([PkgName] -> a -> Command)
511 -> String -> IP.IdrisParser (Either String Command)
512packageBasedCmd valParser cmd name =
514 pkgs <- sepBy (pkg <* IP.whiteSpace) (IP.lchar ',')
517 return (Right (cmd pkgs val)))
518 <|> do val <- valParser
519 return (Right (cmd [] val))
521 pkg = either fail pure . pkgName =<< IP.packageName
523cmd_search :: String -> IP.IdrisParser (Either String Command)
524cmd_search = packageBasedCmd
525 (IP.fullExpr (defaultSyntax { implicitAllowed = True })) Search
527cmd_proofsearch :: String -> IP.IdrisParser (Either String Command)
528cmd_proofsearch name = do
529 upd <- option False (True <$ IP.lchar '!')
530 l <- fmap (fromInteger . fst) IP.natural; n <- fst <$> IP.name
531 hints <- many (fst <$> IP.fnName)
532 return (Right (DoProofSearch upd True l n hints))
534cmd_refine :: String -> IP.IdrisParser (Either String Command)
536 upd <- option False (do IP.lchar '!'; return True)
537 l <- fmap (fromInteger . fst) IP.natural; n <- fst <$> IP.name
538 hint <- fst <$> IP.fnName
539 return (Right (DoProofSearch upd False l n [hint]))
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