-- Defines functions used in evaluating any input which is not a command. module Evaluate (signatureCompletion, evaluate) where import Control.Monad (liftM) import Data.List (find, isPrefixOf, tails, sort) import Data.Map as Map (elems) import Data.Maybe (fromJust) import Language.Haskell.FreeTheorems import Language.Haskell.FreeTheorems.Syntax import System.Console.Shell import System.Console.Shell.ShellMonad import Commands (cmdShowTheorem) import ShellState -- Provides completion support for type names entered directly at the prompt. -- Checks against all known signature names. signatureCompletion :: ShellState -> String -> IO [String] signatureCompletion state input = return $ sort $ filter (isPrefixOf input) (getAllSignatureNames state) -- Evaluate input at the prompt. -- Consider any input starting with a colon as misspelled command name and -- do nothing (it wouldn't be a proper signature, anyway). -- Parse any other input, and if it contains a valid signature, change the -- current context and show the according theorem. evaluate :: String -> Sh ShellState () evaluate s = if ":" `isPrefixOf` s then shellPutStrLn "Ignored incomplete command." else do result <- parseInput s case result of Nothing -> shellSpecial ShellNothing Just sig -> modifyShellSt (setCurrentContext sig) >> cmdShowTheorem -- Parses the input and returns the parsed signature, if any. -- If the input contains "::", then it is is parsed as a signature. -- If the input is only one word, it is looked for in the list of all -- signatures. However, if there is no suitable list or if the input contains -- more than one word, a new name is generated and prepended to the input -- before it is parsed as a signature. parseInput :: String -> Sh ShellState (Maybe ValidSignature) parseInput s = if any (isPrefixOf "::") (tails s) then parseSignature s else case words s of [] -> return Nothing [x] -> do maybeSig <- fromShellSt (findSignature x) case maybeSig of Nothing -> createNewNameAndParse s Just _ -> return maybeSig otherwise -> createNewNameAndParse s where createNewNameAndParse s = do n <- createNewSignatureName parseSignature (n ++ " :: " ++ s) -- Creates a name for a signature which is different to any signature name -- in the list of all signatures of the state. createNewSignatureName :: Sh ShellState String createNewSignatureName = do sigNames <- fromShellSt getAllSignatureNames let newNames = "f":map (\i -> "f" ++ show i) [1..] return $ fromJust $ find (\n -> not $ n `elem` sigNames) newNames -- Tries to parse a string to a signature. -- If successful, the parsed signature is added to the state and its name is -- returned. Otherwise, an error message is printed. parseSignature :: String -> Sh ShellState (Maybe ValidSignature) parseSignature s = do parse <- fromShellSt getParser decls <- fromShellSt (concat . Map.elems . getAllDeclarations) let (vs,es) = runChecks (parse s >>= checkAgainst decls) sig = filterSignatures vs if not (null es) then shellPutErrLn ("Error: " ++ show (head es)) >> return Nothing else case sig of [] -> shellPutErrLn errEmptySig >> return Nothing (s:_) -> do modifyShellSt (addUserSignature s) return (Just s) where errEmptySig = "Error: The input was not recognised as a valid type signature."