---------------------------------------------------------------------- -- | -- Module : Shell -- Maintainer : AR -- Stability : (stable) -- Portability : (portable) -- -- > CVS $Date: 2005/02/25 15:35:48 $ -- > CVS $Author: aarne $ -- > CVS $Revision: 1.34 $ -- -- GF shell command interpreter. ----------------------------------------------------------------------------- module Shell where --- abstract away from these? import Str import qualified Grammar as G import qualified Ident as I import qualified Compute as Co import qualified CheckGrammar as Ch import qualified Lookup as L import qualified GFC import qualified Look import qualified CMacros import qualified GrammarToCanon import Values import GetTree import ShellCommands import VisualizeGrammar (visualizeCanonGrammar, visualizeSourceGrammar) import API import IOGrammar import Compile ---- import GFTex import TeachYourself -- also a subshell import Randomized --- import Editing (goFirstMeta) --- import ShellState import Option import Information import HelpFile import PrOld import PrGrammar import Monad (foldM,liftM) import System (system) import Random (newStdGen) ---- import Zipper ---- import Operations import UseIO import UTF8 (encodeUTF8) import VisualizeGrammar (visualizeSourceGrammar) ---- import qualified GrammarToGramlet as Gr ---- import qualified GrammarToCanonXML2 as Canon -- AR 18/4/2000 - 7/11/2001 -- data Command moved to ShellCommands. AR 27/5/2004 type CommandLine = (CommandOpt, CommandArg, [CommandOpt]) -- | term as returned by the command parser type SrcTerm = G.Term -- | history & CPU type HState = (ShellState,([String],Integer)) type ShellIO = (HState, CommandArg) -> IO (HState, CommandArg) initHState :: ShellState -> HState initHState st = (st,([],0)) cpuHState :: HState -> Integer cpuHState (_,(_,i)) = i optsHState :: HState -> Options optsHState (st,_) = globalOptions st putHStateCPU :: Integer -> HState -> HState putHStateCPU cpu (st,(h,_)) = (st,(h,cpu)) updateHistory :: String -> HState -> HState updateHistory s (st,(h,cpu)) = (st,(s:h,cpu)) -- | empty command if index over earlierCommandH :: HState -> Int -> String earlierCommandH (_,(h,_)) = ((h ++ repeat "") !!) execLinesH :: String -> [CommandLine] -> HState -> IO HState execLinesH s cs hst@(st, (h, _)) = do (_,st') <- execLines True cs hst cpu <- prOptCPU (optsHState st') (cpuHState hst) return $ putHStateCPU cpu $ updateHistory s st' ifImpure :: [CommandLine] -> Maybe (ImpureCommand,Options) ifImpure cls = foldr (const . Just) Nothing [(c,os) | ((CImpure c,os),_,_) <- cls] -- | the main function: execution of commands. 'put :: Bool' forces immediate output -- -- command line with consecutive (;) commands: no value transmitted execLines :: Bool -> [CommandLine] -> HState -> IO ([String],HState) execLines put cs st = foldM (flip (execLine put)) ([],st) cs -- | command line with piped (|) commands: no value returned execLine :: Bool -> CommandLine -> ([String],HState) -> IO ([String],HState) execLine put (c@(co, os), arg, cs) (outps,st) = do (st',val) <- execC c (st, arg) let tr = oElem doTrace os || null cs -- option -tr leaves trace in pipe utf = if (oElem useUTF8 os) then encodeUTF8 else id outp = if tr then [utf (prCommandArg val)] else [] if put then mapM_ putStrLnFlush outp else return () execs cs val (if put then [] else outps ++ outp, st') where execs [] arg st = return st execs (c:cs) arg st = execLine put (c, arg, cs) st -- | individual commands possibly piped: value returned; this is not a state monad execC :: CommandOpt -> ShellIO execC co@(comm, opts0) sa@((st,(h,_)),a) = checkOptions st co >> case comm of CImport file -> useIOE sa $ do st1 <- shellStateFromFiles opts st file ioeIO $ changeState (const st1) sa --- \ ((_,h),a) -> ((st,h), a)) CEmptyState -> changeState reinitShellState sa CChangeMain ma -> changeStateErr (changeMain ma) sa CStripState -> changeState purgeShellState sa {- CRemoveLanguage lan -> changeState (removeLanguage lan) sa CTransformGrammar file -> do s <- transformGrammarFile opts file returnArg (AString s) sa CConvertLatex file -> do s <- readFileIf file returnArg (AString (convertGFTex s)) sa -} CPrintHistory -> (returnArg $ AString $ unlines $ reverse h) sa -- good to have here for piping; eh and ec must be done on outer level CLinearize [] | oElem showMulti opts -> changeArg (opTS2CommandArg (unlines. linearizeToAll (allStateGrammars st)) . s2t) sa | otherwise -> changeArg (opTS2CommandArg (optLinearizeTreeVal opts gro) . s2t) sa ---- CLinearize m -> changeArg (opTS2CommandArg (optLinearizeArgForm opts gro m)) sa CParse ---- | oElem showMulti opts -> do | oElem byLines opts -> do let ss = (if oElem showAll opts then id else filter (not . null)) $ lines $ prCommandArg a mts <- mapM parse ss let a' = ATrms [t | (_,ATrms ts) <- mts, t <- ts] changeArg (const a') sa | otherwise -> parse $ prCommandArg a where parse x = do warnDiscont opts let p = optParseArgErrMsg opts gro x case p of Ok (ts,msg) -> putStrLnFlush msg >> changeArg (const $ ATrms ts) sa Bad msg -> changeArg (const $ AError (msg +++ "input" +++ x)) sa CTranslate il ol -> do let a' = opST2CommandArg (optParseArgErr opts (sgr il)) a returnArg (opTS2CommandArg (optLinearizeTreeVal opts (sgr ol)) a') sa CGenerateRandom -> do let a' = case a of ASTrm _ -> s2t a AString _ -> s2t a _ -> a case a' of ATrms (trm:_) -> case tree2exp trm of G.EInt _ -> do putStrLn "Warning: Number argument deprecated, use gr -number=n instead" ts <- randomTreesIO opts gro (optIntOrN opts flagNumber 1) returnArg (ATrms ts) sa _ -> do g <- newStdGen case (goFirstMeta (tree2loc trm) >>= refineRandom g 41 cgr) of Ok trm' -> returnArg (ATrms [loc2tree trm']) sa Bad s -> returnArg (AError s) sa _ -> do ts <- randomTreesIO opts gro (optIntOrN opts flagNumber 1) returnArg (ATrms ts) sa CGenerateTrees -> do let a' = case a of ASTrm _ -> s2t a AString _ -> s2t a _ -> a mt = case a' of ATrms (tr:_) -> Just tr _ -> Nothing returnArg (ATrms $ generateTrees opts gro mt) sa CPutTerm -> changeArg (opTT2CommandArg (optTermCommand opts gro) . s2t) sa ---- CWrapTerm f -> changeArg (opTT2CommandArg (return . wrapByFun opts gro f)) sa CMorphoAnalyse -> changeArg (AString . morphoAnalyse opts gro . prCommandArg) sa CTestTokenizer -> changeArg (AString . optTokenizer opts gro . prCommandArg) sa CComputeConcrete t -> do m <- return $ maybe (I.identC "?") id $ -- meaningful if no opers in t maybe (resourceOfShellState st) (return . I.identC) $ -- topmost res getOptVal opts useResource -- flag -res=m justOutput opts (putStrLn (err id (prt . stripTerm) ( string2srcTerm src m t >>= Ch.justCheckLTerm src >>= Co.computeConcrete src))) sa CShowOpers t -> do m <- return $ maybe (I.identC "?") id $ -- meaningful if no opers in t maybe (resourceOfShellState st) (return . I.identC) $ -- topmost res getOptVal opts useResource -- flag -res=m justOutput opts (putStrLn (err id (unlines . map prOperSignature) ( string2srcTerm src m t >>= Co.computeConcrete src >>= return . L.opersForType src))) sa CTranslationQuiz il ol -> do warnDiscont opts justOutput opts (teachTranslation opts (sgr il) (sgr ol)) sa CTranslationList il ol n -> do warnDiscont opts qs <- transTrainList opts (sgr il) (sgr ol) (toInteger n) returnArg (AString $ foldr (+++++) [] [unlines (s:ss) | (s,ss) <- qs]) sa CMorphoQuiz -> do warnDiscont opts justOutput opts (teachMorpho opts gro) sa CMorphoList n -> do warnDiscont opts qs <- useIOE [] $ morphoTrainList opts gro (toInteger n) returnArg (AString $ foldr (+++++) [] [unlines (s:ss) | (s,ss) <- qs]) sa CReadFile file -> returnArgIO (readFileIf file >>= return . AString) sa CWriteFile file -> justOutputArg opts (writeFile file) sa CAppendFile file -> justOutputArg opts (appendFile file) sa CSpeakAloud -> justOutputArg opts (speechGenerate opts) sa CSystemCommand s -> justOutput opts (system s >> return ()) sa CPutString -> changeArg (opSS2CommandArg (optStringCommand opts gro)) sa ----- CShowTerm -> changeArg (opTS2CommandArg (optPrintTerm opts gro) . s2t) sa CSetFlag -> changeState (addGlobalOptions opts0) sa ---- deprec! CSetLocalFlag lang -> changeState (addLocalOptions lang opts0) sa CHelp (Just c) -> returnArg (AString (txtHelpCommand c)) sa CHelp _ -> case opts0 of Opts [o] | o == showAll -> returnArg (AString txtHelpFile) sa Opts [o] -> returnArg (AString (txtHelpCommand ('-':prOpt o))) sa _ -> returnArg (AString txtHelpFileSummary) sa CPrintGrammar -> returnArg (AString (optPrintGrammar opts gro)) sa CPrintGlobalOptions -> justOutput opts (putStrLn $ prShellStateInfo st) sa CPrintInformation c -> justOutput opts (useIOE () $ showInformation opts st c) sa CPrintLanguages -> justOutput opts (putStrLn $ unwords $ map prLanguage $ allLanguages st) sa CPrintMultiGrammar -> do sa' <- changeState purgeShellState sa returnArg (AString (optPrintMultiGrammar opts cgr)) sa' CShowGrammarGraph -> do ---- sa' <- changeState purgeShellState sa let g0 = writeFile "grphtmp.dot" $ visualizeCanonGrammar cgr g1 = system "dot -Tps grphtmp.dot >grphtmp.ps" g2 = system "gv grphtmp.ps &" g3 = return () ---- system "rm -f grphtmp.*" justOutput opts (g0 >> g1 >> g2 >> g3 >> return ()) sa CPrintSourceGrammar -> returnArg (AString (visualizeSourceGrammar src)) sa ---- CPrintGramlet -> returnArg (AString (Gr.prGramlet st)) sa ---- CPrintCanonXML -> returnArg (AString (Canon.prCanonXML st False)) sa ---- CPrintCanonXMLStruct -> returnArg (AString (Canon.prCanonXML st True)) sa _ -> justOutput opts (putStrLn "command not understood") sa where sgr = stateGrammarOfLang st gro = grammarOfOptState opts st opts = addOptions opts0 (globalOptions st) src = srcModules st cgr = canModules st s2t a = case a of ASTrm s -> err AError (ATrms . return) $ string2treeErr gro s AString s -> err AError (ATrms . return) $ string2treeErr gro s _ -> a warnDiscont os = err putStrLn id $ do let c0 = firstAbsCat os gro c <- GrammarToCanon.redQIdent c0 lang <- maybeErr "no concrete" $ languageOfOptState os st t <- return $ errVal CMacros.defLinType $ Look.lookupLincat cgr $ CMacros.redirectIdent lang c return $ if CMacros.isDiscontinuousCType t then (putStrLn ("Warning: discontinuous category" +++ prt_ c)) else (return ()) -- commands either change the state or process the argument, but not both -- some commands just do output changeState :: ShellStateOper -> ShellIO changeState f ((st,h),a) = return ((f st,h), a) changeStateErr :: ShellStateOperErr -> ShellIO changeStateErr f ((st,h),a) = case f st of Ok st' -> return ((st',h), a) Bad s -> return ((st, h),AError s) changeArg :: (CommandArg -> CommandArg) -> ShellIO changeArg f (st,a) = return (st, f a) changeArgMsg :: (CommandArg -> (CommandArg,String)) -> ShellIO changeArgMsg f (st,a) = do let (b,msg) = f a putStrLnFlush msg return (st, b) returnArg :: CommandArg -> ShellIO returnArg = changeArg . const returnArgIO :: IO CommandArg -> ShellIO returnArgIO io (st,_) = io >>= (\a -> return (st,a)) justOutputArg :: Options -> (String -> IO ()) -> ShellIO justOutputArg opts f sa@(st,a) = f (utf (prCommandArg a)) >> return (st, AUnit) where utf = if (oElem useUTF8 opts) then encodeUTF8 else id justOutput :: Options -> IO () -> ShellIO justOutput opts = justOutputArg opts . const -- | type system for command arguments; instead of plain strings... data CommandArg = AError String | ATrms [Tree] | ASTrm String -- ^ to receive from parser | AStrs [Str] | AString String | AUnit deriving (Eq, Show) prCommandArg :: CommandArg -> String prCommandArg arg = case arg of AError s -> s AStrs ss -> sstrV ss AString s -> s ATrms [] -> "no tree found" ATrms tt -> unlines $ map prt_Tree tt ASTrm s -> s AUnit -> "" opSS2CommandArg :: (String -> String) -> CommandArg -> CommandArg opSS2CommandArg f = AString . f . prCommandArg opST2CommandArg :: (String -> Err [Tree]) -> CommandArg -> CommandArg opST2CommandArg f = err AError ATrms . f . prCommandArg opTS2CommandArg :: (Tree -> String) -> CommandArg -> CommandArg opTS2CommandArg f (ATrms ts) = AString $ unlines $ map f ts opTS2CommandArg _ (AError s) = AError ("expected term, but got error:" ++++ s) opTS2CommandArg _ a = AError ("expected term, but got:" ++++ prCommandArg a) opTT2CommandArg :: (Tree -> [Tree]) -> CommandArg -> CommandArg opTT2CommandArg f (ATrms ts) = ATrms $ concat $ map f ts opTT2CommandArg _ (AError s) = AError ("expected term, but got error:" ++++ s) opTT2CommandArg _ a = AError ("expected term, but got:" ++++ prCommandArg a)