-- Defines the commands which are not connected to a particular type signature. module Commands where import Control.Monad (liftM, when) import Control.Monad.Trans (liftIO) import Data.List (isPrefixOf, intersperse, sort, transpose) import Data.Map as Map (fromList, keys, lookup, elems) import Data.Maybe (fromJust) import Language.Haskell.FreeTheorems import Language.Haskell.FreeTheorems.Syntax import Language.Haskell.FreeTheorems.Theorems import System.Console.Shell import System.Console.Shell.ShellMonad import Text.PrettyPrint import ShellState ------- File handling --------------------------------------------------------- -- Loads a file of declarations and shows a message of statistics afterwards. cmdLoad :: File -> Sh ShellState () cmdLoad (File fileName) = do state <- getShellSt state' <- liftIO (loadDeclarations fileName state) modifyShellSt (const state') -- Unloads an already loaded file. cmdUnload :: Completable LoadedFile -> Sh ShellState () cmdUnload (Completable file) = do files <- fromShellSt getDeclarationsFiles let removeFile st = st { getDeclarationsFiles = filter (/= file) files } when (file `elem` files) (do modifyShellSt removeFile shellPutStrLn $ "Unloaded `" ++ file ++ "'." cmdReload) -- A tag to identify names of loaded files. data LoadedFile = LoadedFile instance Completion LoadedFile ShellState where complete _ st input = return $ sort $ filter (isPrefixOf input) (getDeclarationsFiles st) completableLabel _ = "" -- Resets the shell state and reloads all files. -- Resetting deletes all previous user input. cmdReload :: Sh ShellState () cmdReload = do files <- fromShellSt getDeclarationsFiles modifyShellSt reset mapM_ (cmdLoad . File) files -- Shows the names of all loaded files. cmdShowLoadedFiles :: Sh ShellState () cmdShowLoadedFiles = do files <- fromShellSt getDeclarationsFiles if null files then shellPutStrLn "There are no loaded files." else mapM_ shellPutStrLn files ------- Declarations ---------------------------------------------------------- -- Displays the names of all valid declarations per input file. -- Names are shown in groups for algebraic data types, type renamings, type -- synonyms, type classes and type signatures. -- Names are shown columnwise for each group. cmdShowAllDeclarations :: Sh ShellState () cmdShowAllDeclarations = do files <- fromShellSt getDeclarationsFiles strings <- mapM fileToString files sigs <- fromShellSt getUserSignatureNames let s = prepend "Type signatures entered by the user:" (inColumns 76 3 2 sigs) let allDecls = strings ++ [s] if and (map null allDecls) then shellPutStrLn "There are no declarations." else mapM_ shellPutStr allDecls where fileToString f = do ds <- mapM (declToString f) declTypes return $ prepend ("In `" ++ f ++ "':") (concat ds) declTypes = [ (" Algebraic data types:", onlyDataDecl) , (" Type renamings:", onlyNewtypeDecl) , (" Type synonyms:", onlyTypeDecl) , (" Type classes:", onlyClassDecl) , (" Type signatures:", onlySigDecl) ] declToString f (label, select) = do ds <- fromShellSt (getDeclarationNamesIn select f) return $ prepend label (inColumns 74 5 2 $ sort ds) prepend _ "" = "" prepend s t = s ++ "\n" ++ t -- Formats a list of strings into a column view. -- The first argument is the length of a line, the second one is the indent to -- prepend every line, and the third argument describes the gap between items. inColumns :: Int -> Int -> Int -> [String] -> String inColumns _ _ _ [] = [] inColumns lineLength indent gap strings = let max = gap + maximum (map length strings) nCols = (lineLength + gap) `div` max guess = (length strings) `div` nCols len = if (length strings) `mod` nCols == 0 then guess else guess + 1 blocks :: Int -> [a] -> [[a]] blocks n xs = if n == 0 then [xs] else case xs of [] -> [] otherwise -> let (s,t) = splitAt n xs in s : (blocks n t) spaces i = replicate i ' ' resize s = s ++ spaces (max - length s) toLine xs = concatMap resize xs shift i s = spaces i ++ s in (unlines . map (shift indent . toLine) . transpose . blocks len) strings -- Shows the declaration for a given name, if it can be found in the list -- of all declarations. cmdShowDeclaration :: Completable DeclarationName -> Sh ShellState () cmdShowDeclaration (Completable name) = do maybeDecl <- fromShellSt (getDeclarationOf name) case maybeDecl of Nothing -> shellPutStrLn $ "There is no declaration with name `" ++ name ++ "'." Just d -> (shellPutStrLn . doRender 0) d -- A tag to identify declaration names. data DeclarationName = DeclarationName instance Completion DeclarationName ShellState where complete _ st input = return $ sort $ filter (isPrefixOf input) (getAllDeclarationNames st) completableLabel _ = "" -- Shows the signatures loaded from input files and previously entered by the -- user. cmdShowSignatures :: Sh ShellState () cmdShowSignatures = do files <- fromShellSt getDeclarationsFiles let fs = map (\f -> "In `" ++ f ++ "':") files sigs <- liftM (zip fs) (mapM (fromShellSt . getSignaturesIn) files) sig <- liftM (\s -> ("Type signatures entered by the user:", s)) (fromShellSt getUserSignatures) let s = filter (\(_,l) -> not (null l)) (sigs ++ [sig]) if null s then shellPutStrLn "There are no type signatures." else mapM_ showSignature s where showSignature (label, sigs) = do shellPutStrLn label let toString = doRender 3 . prettySignature . rawSignature (mapM_ shellPutStrLn . sort . map toString) sigs ------- Language subsets ------------------------------------------------------ -- Shows the current language subset. cmdShowLanguageSubset :: Sh ShellState () cmdShowLanguageSubset = do l <- fromShellSt getCurrentLanguageSubset let name = languageSubsetToString l shellPutStrLn $ "The current language subset is `" ++ name ++ "'." -- Maps language subsets to human-readable strings. languageSubsetToString :: LanguageSubset -> String languageSubsetToString l = case l of BasicSubset -> "basic" SubsetWithFix EquationalTheorem -> "fix-equational" SubsetWithFix InequationalTheorem -> "fix-inequational" SubsetWithSeq EquationalTheorem -> "seq-equational" SubsetWithSeq InequationalTheorem -> "seq-inequational" -- Changes the current language subset and displays the new one afterwards. -- Additionally, if there is a context, an updated theorem is shown. cmdSetLanguageSubset :: LanguageSubset -> Sh ShellState () cmdSetLanguageSubset l = do modifyShellSt (setCurrentLanguageSubset l) context <- fromShellSt getCurrentContext maybe cmdShowLanguageSubset (\_ -> cmdShowTheorem) context ------- Display current theorem and associates -------------------------------- -- Executes a command using the current signature. withCurrentSignature :: (ValidSignature -> Sh ShellState ()) -> Sh ShellState () withCurrentSignature command = do context <- fromShellSt getCurrentContext maybe errorNoSignature (command . fst) context errorNoSignature = shellPutStrLn $ "Please enter a signature or a signature name before using this command." errorNoTheorem = shellPutStrLn $ "There is no theorem possible for the current type in the current language" ++ "\nsubset. Please select a language subset with `seq'." -- Execute a command using the current context. -- The command is applied to the current type signature and the latest -- intermediate generated for that type signature. withCurrentContext :: (ValidSignature -> Intermediate -> Sh ShellState ()) -> Sh ShellState () withCurrentContext command = do context <- fromShellSt getCurrentContext maybe errorNoSignature (\(s,i) -> maybe errorNoTheorem (command s) i) context -- Shows the current type signature. cmdShowCurrentSignature :: Sh ShellState () cmdShowCurrentSignature = withCurrentSignature $ \sig -> do shellPutStrLn . doRender 0 . prettySignature . rawSignature $ sig -- Shows the current theorem. cmdShowTheorem :: Sh ShellState () cmdShowTheorem = withCurrentContext $ \sig intermediate -> do opts <- fromShellSt getCurrentTheoremOptions simp <- fromShellSt getSimplifyOutput l <- fromShellSt getCurrentLanguageSubset let pt = prettyTheorem opts $ (if simp && l `elem` [BasicSubset, SubsetWithFix EquationalTheorem] then simplify else id) $ asTheorem intermediate shellPutStrLn "The free theorem for the type signature\n" (shellPutStrLn . doRender 2 . prettySignature . rawSignature) sig l <- fromShellSt getCurrentLanguageSubset let n = languageSubsetToString l shellPutStrLn $ "\nin the language subset `" ++ n ++ "' is:\n" shellPutStrLn $ (doRender 2 pt) ++ "\n" -- Shows the definitions of lifted relations occurring in the current theorem. cmdShowLiftedRelations :: Sh ShellState () cmdShowLiftedRelations = withCurrentContext $ \_ intermediate -> do vds <- fromShellSt (concat . Map.elems . getAllDeclarations) opts <- fromShellSt getCurrentTheoremOptions simp <- fromShellSt getSimplifyOutput l <- fromShellSt getCurrentLanguageSubset let simpL = if simp && l `elem` [BasicSubset, SubsetWithFix EquationalTheorem] then simplifyUnfoldedLift else id let lifts = unfoldLifts vds intermediate if null lifts then shellPutStrLn "There are no lifted relations in the current theorem." else do shellPutStrLn "" mapM_ (\s -> shellPutStrLn (s ++ "\n")) (map (doRender 2 . prettyUnfoldedLift opts . simpL) lifts) -- Shows the definitions of the class constraints occurring in the current -- theorem. cmdShowClasses :: Sh ShellState () cmdShowClasses = withCurrentContext $ \_ intermediate -> do vds <- fromShellSt (concat . Map.elems . getAllDeclarations) opts <- fromShellSt getCurrentTheoremOptions let classes = unfoldClasses vds intermediate if null classes then shellPutStrLn "There are no class constraints in the current theorem." else do shellPutStrLn "" mapM_ (\s -> shellPutStrLn (s ++ "\n")) (map (doRender 2 . prettyUnfoldedClass opts) classes) ------- Specialising relation variables --------------------------------------- -- Shows all relation variables which can be specialised to functions. cmdShowRelationVariables :: Sh ShellState () cmdShowRelationVariables = withCurrentContext $ \_ intermediate -> do let rvs = relationVariables intermediate in if null rvs then shellPutStrLn $ "There are no relations variables which could be specialised." else shellPutStrLn . doRender 0 . fcat . punctuate (text " ") . map prettyRelationVariable $ rvs -- Specialises a relation variable to a function. cmdSpecialise :: Completable RelationVariable -> Sh ShellState () cmdSpecialise (Completable rv) = withCurrentContext $ \_ intermediate -> do if rv `notElem` map (\(RVar r) -> r) (relationVariables intermediate) then shellPutStrLn $ "Relation variable `" ++ rv ++ "' cannot be specialised." else do modifyShellSt (addSpecialisation (Left (RVar rv))) cmdShowTheorem -- Specialises a relation variable to a function. cmdSpecialiseInverse :: Completable RelationVariable -> Sh ShellState () cmdSpecialiseInverse (Completable rv) = withCurrentContext $ \_ intermediate -> do l <- fromShellSt getCurrentLanguageSubset case l of BasicSubset -> shellPutStrLn error SubsetWithFix EquationalTheorem -> shellPutStrLn error SubsetWithSeq EquationalTheorem -> shellPutStrLn error otherwise -> do if rv `notElem` map (\(RVar r) -> r) (relationVariables intermediate) then shellPutStrLn $ "There is no relation variable `" ++ rv ++ "'." else do modifyShellSt (addSpecialisation (Right (RVar rv))) cmdShowTheorem where error = "This command cannot be used with the current language subset.\n" ++ "Please use an inequational language subset." instance Completion RelationVariable ShellState where complete _ st input = case getCurrentContext st of Nothing -> return [] Just (_,i) -> return . sort . filter (isPrefixOf input) . map (\(RVar r) -> r) $ maybe [] relationVariables i completableLabel _ = "" -- Reverts a specialisation of a relation variable, if at least one -- relation variable has been specialised. cmdUndo :: Sh ShellState () cmdUndo = do h <- fromShellSt getSpecialisationHistory if null h then shellPutStrLn "There is nothing to undo." else do modifyShellSt undoSpecialisation cmdShowTheorem ------- Theorem options ------------------------------------------------------- -- Describes a mapping of theorem options to their human-readable name. allTheoremOptions = Map.fromList [ ("language-subsets", OmitLanguageSubsets) , ("type-instantiations", OmitTypeInstantiations) ] -- Show the currently set theorem options. cmdShowCurrentTheoremOptions :: Sh ShellState () cmdShowCurrentTheoremOptions = do opts <- fromShellSt getCurrentTheoremOptions shellPutStrLn "The current options for displaying theorems are:" mapM_ (showOpt opts) (Map.keys allTheoremOptions) simp <- fromShellSt getSimplifyOutput if simp then shellPutStrLn "The output will be simplified, if possible." else shellPutStrLn "The output will not be simplified." where showOpt opts o = if fromJust (Map.lookup o allTheoremOptions) `elem` opts then shellPutStrLn $ " omit " ++ o else shellPutStrLn $ " show " ++ o -- Enables a theorem option. cmdTheoremOptionShow :: Completable TheoremOptions -> Sh ShellState () cmdTheoremOptionShow = let del opt os = filter (/= opt) os in updateTheoremOptions del -- Disables a theorem option. cmdTheoremOptionOmit :: Completable TheoremOptions -> Sh ShellState () cmdTheoremOptionOmit = let add opt os = if opt `notElem` os then opt : os else os in updateTheoremOptions add -- Toggles the simplify output flag cmdToggleSimplify :: Sh ShellState () cmdToggleSimplify = do modifyShellSt $ \s -> s { getSimplifyOutput = not (getSimplifyOutput s) } simp <- fromShellSt getSimplifyOutput if simp then shellPutStrLn "The output will be simplified, if possible." else shellPutStrLn "The output will not be simplified." -- Modifies the current theorem options with the help of a modification -- function. updateTheoremOptions :: (PrettyTheoremOption -> [PrettyTheoremOption] -> [PrettyTheoremOption]) -> Completable TheoremOptions -> Sh ShellState () updateTheoremOptions f (Completable opt) = case Map.lookup opt allTheoremOptions of Nothing -> shellPutErrLn $ "Unknown theorem option `" ++ opt ++ "'." Just o -> do modifyShellSt (updateOptions (f o)) cmdShowCurrentTheoremOptions ctx <- fromShellSt getCurrentContext maybe (shellSpecial ShellNothing) (\_ -> shellPutStrLn "" >> cmdShowTheorem) ctx where updateOptions f state = state { getCurrentTheoremOptions = f (getCurrentTheoremOptions state) } -- A tag to identify theorem options. data TheoremOptions = TheoremOptions instance Completion TheoremOptions ShellState where complete _ st input = return $ sort $ filter (isPrefixOf input) (Map.keys allTheoremOptions) completableLabel _ = "