ftshell-0.2: src/Evaluate.hs
-- 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."