Files
gf-core/src/GF/Shell/Commands.hs
2004-05-02 12:36:19 +00:00

481 lines
16 KiB
Haskell

module Commands where
import Operations
import Zipper
import qualified Grammar as G ---- Cat, Fun, Q, QC
import GFC
import CMacros
import Macros (qq)----
import LookAbs
import Look
import Values (loc2treeFocus,tree2exp)----
import GetTree
import API
import ShellState
import qualified Shell
import qualified PShell
import qualified Macros as M
import PrGrammar
import PGrammar
import IOGrammar
import UseIO
import Unicode
import CF
import CFIdent (cat2CFCat, cfCat2Cat)
import Linear
import Randomized
import Editing
import Session
import Custom
import qualified Ident as I
import Option
import Str (sstr) ----
import UTF8 ----
import Random (mkStdGen, newStdGen)
import Monad (liftM2, foldM)
import List (intersperse)
--- temporary hacks for GF 2.0
-- Abstract command language for syntax editing. AR 22/8/2001
-- Most arguments are strings, to make it easier to receive them from e.g. Java.
-- See CommandsL for a parser of a command language.
data Command =
CNewCat String
| CNewTree String
| CAhead Int
| CBack Int
| CNextMeta
| CPrevMeta
| CTop
| CLast
| CMovePosition [Int]
| CRefineWithTree String
| CRefineWithClip Int
| CRefineWithAtom String
| CRefineParse String
| CWrapWithFun (String,Int)
| CChangeHead String
| CPeelHead
| CAlphaConvert String
| CRefineRandom
| CSelectCand Int
| CTermCommand String
| CAddOption Option
| CRemoveOption Option
| CDelete
| CAddClip
| CUndo
| CView
| CMenu
| CQuit
| CHelp (CEnv -> String) -- help message depends on grammar and interface
| CError -- syntax error in command
| CVoid -- empty command, e.g. just <enter>
-- commands affecting CEnv
| CCEnvImport String
| CCEnvEmptyAndImport String
| CCEnvOpenTerm String
| CCEnvOpenString String
| CCEnvEmpty
| CCEnvOn String
| CCEnvOff String
| CCEnvGFShell String
-- other commands using IO
| CCEnvRefineWithTree String
| CCEnvRefineParse String
isQuit CQuit = True
isQuit _ = False
-- an abstract environment type
type CEnv = ShellState
grammarCEnv = firstStateGrammar
canCEnv = canModules
concreteCEnv = cncId
abstractCEnv = absId
stdGenCEnv env s = mkStdGen (length (displayJustStateIn env s) * 31 +11) ---
initSStateEnv env = case getOptVal (stateOptions sgr) gStartCat of
Just cat -> action2commandNext (newCat gr (abs, I.identC cat)) initSState
_ -> initSState
where
sgr = firstStateGrammar env
abs = absId sgr
gr = stateGrammarST sgr
-- the main function
execCommand :: CEnv -> Command -> SState -> IO (CEnv,SState)
execCommand env c s = case c of
-- these commands do need IO
CCEnvImport file -> useIOE (env,s) $ do
st <- shellStateFromFiles opts env file
return (st,s)
CCEnvEmptyAndImport file -> useIOE (emptyShellState, initSState) $ do
st <- shellStateFromFiles opts emptyShellState file
return (st,s)
CCEnvEmpty -> do
return (emptyShellState, initSState)
CCEnvGFShell command -> do
let cs = PShell.pCommandLines command
(msg,(env',_)) <- Shell.execLines False cs (Shell.initHState env)
return (env', changeMsg msg s) ----
CCEnvOpenTerm file -> do
c <- readFileIf file
let (fs,t) = envAndTerm file c
env' <- useIOE env $ foldM (shellStateFromFiles noOptions) env fs
return (env', execECommand env' (CNewTree t) s)
CCEnvOpenString file -> do
c <- readFileIf file
let (fs,t) = envAndTerm file c
env' <- useIOE env $ foldM (shellStateFromFiles noOptions) env fs
return (env', execECommand env' (CRefineParse t) s)
CCEnvOn name -> return (languageOn (language name) env,s)
CCEnvOff name -> return (languageOff (language name) env,s)
-- this command is improved by the use of IO
CRefineRandom -> do
g <- newStdGen
return (env, action2commandNext (refineRandom g 41 cgr) s)
-- these commands use IO
CCEnvRefineWithTree file -> do
str <- readFileIf file
execCommand env (CRefineWithTree str) s
CCEnvRefineParse file -> do
str <- readFileIf file
execCommand env (CRefineParse str) s
-- other commands don't need IO; they are available in the fudget
c -> return (env, execECommand env c s)
where
gr = grammarCEnv env
cgr = canCEnv env
opts = globalOptions env
-- format for documents: import lines of form "-- file", then term
envAndTerm f s =
(map ((initFilePath f ++) . filter (/=' ') . drop 2) fs, unlines ss) where
(fs,ss) = span isImport (lines s)
isImport l = take 2 l == "--"
execECommand :: CEnv -> Command -> ECommand
execECommand env c = case c of
CNewCat cat -> action2commandNext $ \x -> do
cat' <- string2cat sgr cat
s' <- newCat cgr cat' x
uniqueRefinements cgr s'
CNewTree s -> action2commandNext $ \x -> do
t <- string2treeErr gr s
s' <- newTree t x
uniqueRefinements cgr s'
CAhead n -> action2command (goAheadN n)
CBack n -> action2command (goBackN n)
CTop -> action2command $ return . goRoot
CLast -> action2command $ goLast
CMovePosition p -> action2command $ goPosition p
CNextMeta -> action2command goNextNewMeta
CPrevMeta -> action2command goPrevNewMeta
CRefineWithAtom s -> action2commandNext $ \x -> do
t <- string2ref gr s
s' <- refineWithAtom der cgr t x
uniqueRefinements cgr s'
CWrapWithFun (f,i) -> action2commandNext $ wrapWithFun cgr (qualif f, i)
CChangeHead f -> action2commandNext $ changeFunHead cgr (qualif f)
CPeelHead -> action2commandNext $ peelFunHead cgr
CAlphaConvert s -> action2commandNext $ \x ->
string2varPair s >>= \xy -> alphaConvert cgr xy x
CRefineWithTree s -> action2commandNext $ \x ->
(string2treeInState gr s x >>=
\t -> refineWithTree der cgr t x)
CRefineWithClip i -> \s ->
let et = getNumberedClip i s
in (case et of
Ok t -> refineByTrees der cgr [t] s
Bad m -> changeMsg [m] s)
CRefineParse str -> \s ->
let cat = cat2CFCat (qualifTop sgr (actCat (stateSState s)))
ts = parseAny agrs cat str
in (if null ts ---- debug
then withMsg [str, "parse failed in cat" +++ show cat]
else id)
(refineByTrees der cgr ts) s
CRefineRandom -> \s -> action2commandNext
(refineRandom (stdGenCEnv env s) 41 cgr) s
CSelectCand i -> selectCand cgr i
CTermCommand c -> case c of
"paraphrase" -> \s ->
replaceByTermCommand der gr c (actTree (stateSState s)) s
---- "transfer" -> action2commandNext $
---- transferSubTree (stateTransferFun sgr) gr
"generate" -> \s ->
replaceByTermCommand der gr c (actTree (stateSState s)) s
_ -> replaceByEditCommand gr c
CAddOption o -> changeStOptions (addOption o)
CRemoveOption o -> changeStOptions (removeOption o)
CDelete -> action2commandNext $ deleteSubTree cgr
CAddClip -> \s -> (addtoClip (actTree (stateSState s))) s
CUndo -> undoCommand
CMenu -> \s -> changeMsg (menuState env s) s
CView -> changeView
CHelp h -> changeMsg [h env]
CVoid -> id
_ -> changeMsg ["command not yet implemented"]
where
sgr = firstStateGrammar env
agrs = allActiveGrammars env
cgr = canCEnv env
gr = grammarCEnv env
der = maybe True not $ caseYesNo (globalOptions env) noDepTypes
-- if there are dep types, then derived refs; deptypes is the default
abs = absId sgr
qualif = string2Fun gr
--
string2varPair :: String -> Err (I.Ident,I.Ident)
string2varPair s = case words s of
x : y : [] -> liftM2 (,) (string2ident x) (string2ident y)
_ -> Bad "expected format 'x y'"
-- seen on display
cMenuDisplay :: String -> Command
cMenuDisplay s = CAddOption (menuDisplay s)
newCatMenu env = [(CNewCat (prQIdent c), printname env initSState c) |
(c,[]) <- allCatsOf (canCEnv env)]
mkRefineMenu :: CEnv -> SState -> [(Command,String)]
mkRefineMenu env sstate = [(c,s) | (c,(s,_)) <- mkRefineMenuAll env sstate]
mkRefineMenuAll :: CEnv -> SState -> [(Command,(String,String))]
mkRefineMenuAll env sstate =
case (refinementsState cgr state, candsSState sstate, wrappingsState cgr state) of
([],[],wraps) ->
[(CWrapWithFun (prQIdent_ f, i), prWrap fit)
| fit@((f,i),_) <- wraps] ++
[(CChangeHead (prQIdent_ f), prChangeHead f)
| f <- headChangesState cgr state] ++
[(CPeelHead, (ifShort "ph" "PeelHead", "ph"))
| canPeelState cgr state] ++
[(CDelete, (ifShort "d" "Delete", "d"))] ++
[(CAddClip, (ifShort "ac" "AddClip", "ac"))]
(refs,[],_) ->
[(CRefineWithAtom (prRefinement f), prRef t) | t@(f,_) <- refs] ++
[(CRefineWithClip i, prClip i t) | (i,t) <- possClipsSState gr sstate]
(_,cands,_) ->
[(CSelectCand i, prCand (t,i)) | (t,i) <- zip cands [0..]]
where
prRef (f,t) =
(ifShort "r" "Refine" +++ prOrLinRef f +++ ifTyped (":" +++ prt_ t),
"r" +++ prRefinement f)
prClip i t =
(ifShort "rc" "Paste" +++ prOrLinTree t,
"rc" +++ show i)
prChangeHead f =
(ifShort "ch" "ChangeHead" +++ prOrLinFun f,
"ch" +++ prQIdent_ f)
prWrap ((f,i),t) =
(ifShort "w" "Wrap" +++ prOrLinFun f +++ ifTyped (":" +++ prt t) +++
ifShort (show i) (prBracket (show i)),
"w" +++ prQIdent_ f +++ show i)
prCand (t,i) =
(ifShort ("s" +++ prOrLinExp t) ("Select" +++ prOrLinExp t),"s" +++ show i)
gr = grammarCEnv env
cgr = canCEnv env
state = stateSState sstate
opts = addOptions (optsSState sstate) (globalOptions env)
ifOpt f v a b = case getOptVal opts f of
Just s | s == v -> a
_ -> b
ifShort = ifOpt sizeDisplay "short"
ifTyped t = ifOpt typeDisplay "typed" t ""
prOrLinExp t = err (const $ prt_ t) prOrLinTree $ annotateInState cgr t state
prOrLinRef t = case t of
G.Q m f -> printname env sstate (m,f)
G.QC m f -> printname env sstate (m,f)
_ -> prt_ t
prOrLinFun = printname env sstate
prOrLinTree t = case getOptVal opts menuDisplay of
Just "Abs" -> prt_ $ tree2exp t ---- prTermOpt opts $ tree2exp t
Just lang -> prQuotedString $ lin lang t
_ -> prTermOpt opts $ tree2exp t
lin lang t = optLinearizeTreeVal opts (stateGrammarOfLang env (language lang)) t
-- there are three orthogonal parameters: Abs/[conc], short/long, typed/untyped
-- the default is Abs, long, untyped; the Menus menu changes the parameter
emptyMenuItem = (CVoid,("",""))
---- allStringCommands = snd $ customInfo customStringCommand
termCommandMenu, stringCommandMenu :: [(Command,String)]
termCommandMenu = [(CTermCommand s, s) | s <- allTermCommands]
allTermCommands = snd $ customInfo customEditCommand
stringCommandMenu = []
displayCommandMenu :: CEnv -> [(Command,String)]
displayCommandMenu env =
[(CAddOption (menuDisplay s), s) | s <- "Abs" : langs] ++
[(CAddOption (sizeDisplay s), s) | s <- ["short", "long"]] ++
[(fo nostripQualif, s) | (fo,s) <- [(CAddOption,"qualified"),
(CRemoveOption,"unqualified")]] ++
[(CAddOption (typeDisplay s), s) | s <- ["typed", "untyped"]]
where
langs = map prLanguage $ allLanguages env
{- ----
stringCommandMenu =
(CAddOption showStruct, "structured") :
(CRemoveOption showStruct, "unstructured") :
[(CAddOption (filterString s), s) | s <- allStringCommands]
-}
changeMenuLanguage, changeMenuSize, changeMenuTyped :: String -> Command
changeMenuLanguage s = CAddOption (menuDisplay s)
changeMenuSize s = CAddOption (sizeDisplay s)
changeMenuTyped s = CAddOption (typeDisplay s)
menuState env = map snd . mkRefineMenu env
prState :: State -> [String]
prState s = prMarkedTree (loc2treeMarked s)
displayJustStateIn :: CEnv -> SState -> String
displayJustStateIn env state = case displaySStateIn env state of
(t,msg,_) -> unlines (t ++ ["",""] ++ msg) --- ad hoc for CommandF
displaySStateIn :: CEnv -> SState -> ([String],[String],[(String,String)])
displaySStateIn env state = (tree',msg,menu) where
(tree,msg,menu) = displaySState env state
grs = allStateGrammars env
lang = (viewSState state) `mod` (length grs + 3)
tree' = (tree : exp : linAll ++ separ (linAll ++ [tree])) !! lang
opts = addOptions (optsSState state) -- state opts override
(addOption (markLin markOptFocus) (globalOptions env))
lin g = linearizeState fudWrap opts g zipper
exp = return $ tree2string $ loc2tree zipper
zipper = stateSState state
linAll = map lin grs
separ = singleton . map unlines . intersperse [replicate 72 '*']
---- the Boolean is a temporary hack to have two parallel GUIs
displaySStateJavaX :: Bool -> CEnv -> SState -> String
displaySStateJavaX isNew env state = encodeUTF8 $ mkUnicode $
unlines $ tagXML "gfedit" $ concat [
tagXML "linearizations" (concat
[tagAttrXML "lin" ("lang", prLanguage lang) ss | (lang,ss) <- lins]),
tagXML "tree" tree,
tagXML "message" msg,
tagXML "menu" (tagsXML "item" menu')
]
where
(tree,msg,menu) = displaySState env state
menu' = [tagXML "show" [unicode s] ++ tagXML "send" [c] | (s,c) <- menu]
(ls,grs) = unzip $ lgrs
lgrs = allActiveStateGrammarsWithNames env
lins = (langAbstract, exp) : linAll
opts = addOptions (optsSState state) -- state opts override
(addOption (markLin mark) (globalOptions env))
lin (n,gr) = (n, map uni $ linearizeState noWrap opts gr zipper) where
uni = optDecodeUTF8 gr
exp = prprTree $ loc2tree zipper
zipper = stateSState state
linAll = map lin lgrs
gr = firstStateGrammar env
mark = markOptXML -- markOptJava
unicode = case getOptVal opts menuDisplay of
Just lang -> optDecodeUTF8 (stateGrammarOfLang env (language lang))
_ -> id
langAbstract = language "Abstract"
langXML = language "XML"
linearizeState :: (String -> [String]) -> Options -> GFGrammar -> State -> [String]
linearizeState wrap opts gr =
wrap . strop . unt . optLinearizeTreeVal opts gr . loc2treeFocus
where
unt = customOrDefault (stateOptions gr) useUntokenizer customUntokenizer gr
strop = maybe id ($ gr) $ customAsOptVal opts filterString customStringCommand
br = oElem showStruct opts
noWrap, fudWrap :: String -> [String]
noWrap = lines
fudWrap = lines . wrapLines 0 ---
displaySState :: CEnv -> SState -> ([String],[String],[(String,String)])
displaySState env state =
(prState (stateSState state), msgSState state, menuSState env state)
menuSState :: CEnv -> SState -> [(String,String)]
menuSState env state = [(s,c) | (_,(s,c)) <- mkRefineMenuAll env state]
printname :: CEnv -> SState -> G.Fun -> String
printname env state f = case getOptVal opts menuDisplay of
Just "Abs" -> prQIdent_ f
Just lang -> printn lang f
_ -> prQIdent_ f ---- prTermOpt opts (qq f)
where
opts = addOptions (optsSState state) (globalOptions env)
printn lang f = err id (ifNull (prQIdent_ f) (sstr . head)) $ do
t <- lookupPrintname gr mf
strsFromTerm t
where
sgr = stateGrammarOfLang env (language lang)
gr = grammar sgr
mf = ciq (cncId sgr) (snd f)
--- XML printing; does not belong here!
tagsXML t = concatMap (tagXML t)
tagAttrXML t av ss = mkTagAttrXML t av : map (indent 2) ss ++ [mkEndTagXML t]
tagXML t ss = mkTagXML t : map (indent 2) ss ++ [mkEndTagXML t]
mkTagXML t = '<':t ++ ">"
mkEndTagXML t = mkTagXML ('/':t)
mkTagAttrsXML t avs = '<':t +++ unwords [a++"="++v | (a,v) <- avs] ++">"
mkTagAttrXML t av = mkTagAttrsXML t [av]