module Agda.Interaction.GhciTop
( module Agda.Interaction.GhciTop
, module Agda.TypeChecker
, module Agda.TypeChecking.MetaVars
, module Agda.TypeChecking.Reduce
, module Agda.TypeChecking.Errors
, module Agda.Syntax.Position
, module Agda.Syntax.Parser
, module Agda.Syntax.Scope.Base
, module Agda.Syntax.Scope.Monad
, module Agda.Syntax.Translation.ConcreteToAbstract
, module Agda.Syntax.Translation.AbstractToConcrete
, module Agda.Syntax.Translation.InternalToAbstract
, module Agda.Syntax.Abstract.Name
, module Agda.Interaction.Exceptions
)
where
import System.Directory
import System.IO.Unsafe
import Data.Char
import Data.IORef
import Data.Function
import Control.Applicative
import qualified System.IO.UTF8 as UTF8
import Agda.Utils.Fresh
import Agda.Utils.Monad
import Agda.Utils.Monad.Undo
import Agda.Utils.Pretty as P
import Agda.Utils.String
import Agda.Utils.FileName
import Agda.Utils.Tuple
import Control.Monad.Error
import Control.Monad.Reader
import Control.Monad.State hiding (State)
import Control.Exception
import Data.List as List
import qualified Data.Map as Map
import System.Exit
import qualified System.Mem as System
import Agda.TypeChecker
import Agda.TypeChecking.Monad as TM
hiding (initState, setCommandLineOptions)
import qualified Agda.TypeChecking.Monad as TM
import Agda.TypeChecking.MetaVars
import Agda.TypeChecking.Reduce
import Agda.TypeChecking.Errors
import Agda.TypeChecking.Serialise (encodeFile)
import Agda.Syntax.Position
import Agda.Syntax.Parser
import qualified Agda.Syntax.Parser.Tokens as T
import Agda.Syntax.Concrete as SC
import Agda.Syntax.Common as SCo
import Agda.Syntax.Concrete.Name as CN
import Agda.Syntax.Concrete.Pretty ()
import Agda.Syntax.Abstract as SA
import Agda.Syntax.Abstract.Pretty
import Agda.Syntax.Internal as SI
import Agda.Syntax.Scope.Base
import Agda.Syntax.Scope.Monad hiding (bindName, withCurrentModule)
import qualified Agda.Syntax.Info as Info
import Agda.Syntax.Translation.ConcreteToAbstract
import Agda.Syntax.Translation.AbstractToConcrete hiding (withScope)
import Agda.Syntax.Translation.InternalToAbstract
import Agda.Syntax.Abstract.Name
import Agda.Interaction.Exceptions
import Agda.Interaction.Options
import Agda.Interaction.MakeCase
import qualified Agda.Interaction.BasicOps as B
import qualified Agda.Interaction.CommandLine.CommandLine as CL
import Agda.Interaction.Highlighting.Emacs
import Agda.Interaction.Highlighting.Generate
import qualified Agda.Interaction.Imports as Imp
import Agda.Termination.TermCheck
import qualified Agda.Compiler.MAlonzo.Compiler as MAlonzo
#include "../undefined.h"
import Agda.Utils.Impossible
data State = State
{ theTCState :: TCState
, theUndoStack :: [TCState]
, theTopLevel :: Maybe TopLevelInfo
, theInteractionPoints :: [InteractionId]
}
initState :: State
initState = State
{ theTCState = TM.initState
, theUndoStack = []
, theTopLevel = Nothing
, theInteractionPoints = []
}
theState :: IORef State
theState = unsafePerformIO $ newIORef initState
ioTCM :: TCM a -> IO a
ioTCM cmd = infoOnException $ do
State { theTCState = st
, theUndoStack = us
} <- readIORef theState
r <- runTCM $ do
putUndoStack us
put st
x <- withEnv initEnv cmd
st <- get
us <- getUndoStack
return (x,st,us)
case r of
Right (a,st',ss') -> do
modifyIORef theState $ \s ->
s { theTCState = st'
, theUndoStack = ss'
}
return a
Left err -> displayErrorAndExit (Left err)
cmd_load :: FilePath -> [FilePath] -> IO ()
cmd_load m includes =
cmd_load' m includes True (\_ -> return ()) cmd_metas
cmd_load' :: FilePath -> [FilePath]
-> Bool
-> (Imp.CreateInterfaceResult -> TCM ()) -> IO ()
-> IO ()
cmd_load' file includes unsolvedOK cmd cmd2 = infoOnException $ do
file <- liftIO $ canonicalizePath file
clearSyntaxInfo file
ioTCM $ do
clearUndoHistory
preserveDecodedModules resetState
decodedModules <- stDecodedModules <$> get
setUndo
(topLevel, ok) <- Imp.createInterface
(defaultOptions { optGenerateEmacsFile = True
, optAllowUnsolved = unsolvedOK
, optIncludeDirs = includes })
noTrace [] Map.empty
decodedModules emptySignature
Map.empty Nothing file True
is <- sortInteractionPoints =<< getInteractionPoints
liftIO $ modifyIORef theState $ \s ->
s { theTopLevel = Just topLevel
, theInteractionPoints = is
}
cmd ok
liftIO tellEmacsToReloadSyntaxInfo
cmd2
System.performGC
cmd_compile :: FilePath -> [FilePath] -> IO ()
cmd_compile file includes =
cmd_load' file includes False (\r ->
case r of
Imp.Success { Imp.cirInterface = i } -> do
MAlonzo.compilerMain (return i)
display_info "*Compilation result*"
"The module was successfully compiled."
Imp.Warnings {} ->
display_info errorTitle $ unlines
[ "You can only compile modules without unsolved metavariables"
, "or termination checking problems."
])
(return ())
cmd_constraints :: IO ()
cmd_constraints = ioTCM $ do
cs <- map show <$> B.getConstraints
display_info "*Constraints*" (unlines cs)
cmd_metas :: IO ()
cmd_metas = ioTCM $ do
ims <- fst <$> B.typeOfMetas B.AsIs
hms <- snd <$> B.typeOfMetas B.Normalised
di <- mapM (\i -> B.withInteractionId (B.outputFormId i) (showA i)) ims
dh <- mapM showA' hms
display_info "*All Goals*" $ unlines $ di ++ dh
where
metaId (B.OfType i _) = i
metaId (B.JustType i) = i
metaId (B.JustSort i) = i
metaId (B.Assign i e) = i
metaId _ = __IMPOSSIBLE__
showA' m = do
r <- getMetaRange (metaId m)
d <- B.withMetaId (B.outputFormId m) (showA m)
return $ d ++ " [ at " ++ show r ++ " ]"
cmd_undo :: IO ()
cmd_undo = ioTCM undo
cmd_reset :: IO ()
cmd_reset = ioTCM $ do putUndoStack []; preserveDecodedModules resetState
type GoalCommand = InteractionId -> Range -> String -> IO()
cmd_give :: GoalCommand
cmd_give = give_gen B.give $ \s ce -> case ce of (SC.Paren _ _)-> "'paren"
_ -> "'no-paren"
cmd_refine :: GoalCommand
cmd_refine = give_gen B.refine $ \s -> emacsStr . show
give_gen give_ref mk_newtxt ii rng s = do
ioTCM $ do
scope <- getInteractionScope ii
(ae, iis) <- give_ref ii Nothing =<< B.parseExprIn ii rng s
let newtxt = A . mk_newtxt s $ abstractToConcrete (makeEnv scope) ae
iis <- sortInteractionPoints iis
liftIO $ modifyIORef theState $ \s ->
s { theInteractionPoints =
replace ii iis (theInteractionPoints s) }
liftIO $ UTF8.putStrLn $ response $
L [A "agda2-give-action", showNumIId ii, newtxt]
liftIO tellEmacsToUpdateGoals
cmd_metas
where
replace x xs ys = concatMap (\y -> if y == x then xs else [y]) ys
sortInteractionPoints :: [InteractionId] -> TCM [InteractionId]
sortInteractionPoints is =
map fst . sortBy (compare `on` snd) <$>
mapM (\i -> (,) i <$> getInteractionRange i) is
prettyTypeOfMeta :: B.Rewrite -> InteractionId -> TCM Doc
prettyTypeOfMeta norm ii = do
form <- B.typeOfMeta norm ii
case form of
B.OfType _ e -> prettyA e
_ -> text <$> showA form
prettyContext
:: B.Rewrite
-> InteractionId
-> TCM Doc
prettyContext norm ii = B.withInteractionId ii $ do
ctx <- B.contextOfMeta ii norm
es <- mapM (prettyA . B.ofExpr) ctx
ns <- mapM (showA . B.ofName) ctx
let maxLen = maximum $ 0 : filter (< longNameLength) (map length ns)
return $ vcat $
map (\(n, e) -> text n $$ nest (maxLen + 1) (text ":") <+> e) $
zip ns es
longNameLength = 10
cmd_context :: B.Rewrite -> GoalCommand
cmd_context norm ii _ _ = ioTCM $
display_infoD "*Context*" =<< prettyContext norm ii
cmd_infer :: B.Rewrite -> GoalCommand
cmd_infer norm ii rng s = ioTCM $
display_infoD "*Inferred Type*"
=<< B.withInteractionId ii
(prettyA =<< B.typeInMeta ii norm =<< B.parseExprIn ii rng s)
cmd_goal_type :: B.Rewrite -> GoalCommand
cmd_goal_type norm ii _ _ = ioTCM $ do
s <- B.withInteractionId ii $ prettyTypeOfMeta norm ii
display_infoD "*Current Goal*" s
cmd_goal_type_context_and :: Doc -> B.Rewrite -> GoalCommand
cmd_goal_type_context_and s norm ii _ _ = ioTCM $ do
goal <- B.withInteractionId ii $ prettyTypeOfMeta norm ii
ctx <- prettyContext norm ii
display_infoD "*Goal type etc.*"
(ctx $+$
text (replicate 60 '\x2014') $+$
text "Goal:" <+> goal $+$
s)
cmd_goal_type_context :: B.Rewrite -> GoalCommand
cmd_goal_type_context = cmd_goal_type_context_and P.empty
cmd_goal_type_context_infer :: B.Rewrite -> GoalCommand
cmd_goal_type_context_infer norm ii rng s = ioTCM $ do
typ <- B.withInteractionId ii $
prettyA =<< B.typeInMeta ii norm =<< B.parseExprIn ii rng s
liftIO $ cmd_goal_type_context_and
(text "Have:" <+> typ)
norm ii rng s
setCommandLineOptions :: CommandLineOptions -> TCM ()
setCommandLineOptions opts = do
TM.setCommandLineOptions opts
displayStatus
displayStatus :: TCM ()
displayStatus = do
showImpl <- showImplicitArguments
let statusString = if showImpl then "ShowImplicit" else ""
liftIO $ UTF8.putStrLn $ response $
L [A "agda2-status-action", A (quote statusString)]
display_info :: String -> String -> TCM ()
display_info bufname content = do
displayStatus
liftIO $ UTF8.putStrLn $ response $
L [ A "agda2-info-action"
, A (quote bufname)
, A (quote content)
]
display_infoD :: String -> Doc -> TCM ()
display_infoD bufname content = display_info bufname (render content)
response :: Lisp String -> String
response l = show (text "agda2_mode_code" <+> pretty l)
data Lisp a = A a | L [Lisp a] | Q (Lisp a)
instance Pretty a => Pretty (Lisp a) where
pretty (A a ) = pretty a
pretty (L xs) = parens (sep (List.map pretty xs))
pretty (Q x) = text "'"<>pretty x
instance Pretty String where pretty = text
instance Pretty a => Show (Lisp a) where show = show . pretty
showNumIId = A . tail . show
takenNameStr :: TCM [String]
takenNameStr = do
xss <- sequence [ List.map (fst . unArg) <$> getContext
, Map.keys <$> asks envLetBindings
, List.map qnameName . Map.keys . sigDefinitions <$> getSignature
]
return $ concat [ parts $ nameConcrete x | x <- concat xss]
where
parts x = [ s | Id s <- nameParts x ]
refreshStr :: [String] -> String -> ([String], String)
refreshStr taken s = go nameModifiers where
go (m:mods) = let s' = s ++ m in
if s' `elem` taken then go mods else (s':taken, s')
go _ = __IMPOSSIBLE__
nameModifiers = "" : "'" : "''" : [show i | i <-[3..]]
cmd_make_case :: GoalCommand
cmd_make_case ii rng s = ioTCM $ do
cs <- makeCase ii rng s
B.withInteractionId ii $ do
pcs <- mapM prettyA cs
liftIO $ UTF8.putStrLn $ response $
L [ A "agda2-make-case-action",
Q $ L $ List.map (A . quote . show) pcs
]
cmd_solveAll :: IO ()
cmd_solveAll = ioTCM $ do
out <- getInsts
liftIO $ UTF8.putStrLn $ response $
L[ A"agda2-solveAll-action" , Q . L $ concatMap prn out]
where
getInsts = mapM lowr =<< B.getSolvedInteractionPoints
where
lowr (i, m, e) = do
mi <- getMetaInfo <$> lookupMeta m
e <- withMetaInfo mi $ lowerMeta <$> abstractToConcrete_ e
return (i, e)
prn (ii,e)= [showNumIId ii, A $ emacsStr $ show e]
class LowerMeta a where lowerMeta :: a -> a
instance LowerMeta SC.Expr where
lowerMeta = go where
go e = case e of
Ident _ -> e
SC.Lit _ -> e
SC.QuestionMark _ _ -> preMeta
SC.Underscore _ _ -> preUscore
SC.App r e1 ae2 -> case appView e of
SC.AppView (SC.QuestionMark _ _) _ -> preMeta
SC.AppView (SC.Underscore _ _) _ -> preUscore
_ -> SC.App r (go e1) (lowerMeta ae2)
SC.WithApp r e es -> SC.WithApp r (lowerMeta e) (lowerMeta es)
SC.Lam r bs e1 -> SC.Lam r (lowerMeta bs) (go e1)
SC.AbsurdLam r h -> SC.AbsurdLam r h
SC.Fun r ae1 e2 -> SC.Fun r (lowerMeta ae1) (go e2)
SC.Pi tb e1 -> SC.Pi (lowerMeta tb) (go e1)
SC.Set _ -> e
SC.Prop _ -> e
SC.SetN _ _ -> e
SC.ETel tel -> SC.ETel (lowerMeta tel)
SC.Let r ds e1 -> SC.Let r (lowerMeta ds) (go e1)
Paren r e1 -> case go e1 of
q@(SC.QuestionMark _ Nothing) -> q
e2 -> Paren r e2
Absurd _ -> e
As r n e1 -> As r n (go e1)
SC.Dot r e -> SC.Dot r (go e)
SC.RawApp r es -> SC.RawApp r (lowerMeta es)
SC.OpApp r x es -> SC.OpApp r x (lowerMeta es)
SC.Rec r fs -> SC.Rec r (List.map (id -*- lowerMeta) fs)
SC.HiddenArg r e -> SC.HiddenArg r (lowerMeta e)
instance LowerMeta SC.LamBinding where
lowerMeta b@(SC.DomainFree _ _) = b
lowerMeta (SC.DomainFull tb) = SC.DomainFull (lowerMeta tb)
instance LowerMeta SC.TypedBindings where
lowerMeta (SC.TypedBindings r h bs) = SC.TypedBindings r h (lowerMeta bs)
instance LowerMeta SC.TypedBinding where
lowerMeta (SC.TBind r ns e) = SC.TBind r ns (lowerMeta e)
lowerMeta (SC.TNoBind e) = SC.TNoBind (lowerMeta e)
instance LowerMeta SC.RHS where
lowerMeta (SC.RHS e) = SC.RHS (lowerMeta e)
lowerMeta SC.AbsurdRHS = SC.AbsurdRHS
instance LowerMeta SC.Declaration where
lowerMeta = go where
go d = case d of
TypeSig n e1 -> TypeSig n (lowerMeta e1)
SC.Field n e1 -> SC.Field n (lowerMeta e1)
FunClause lhs rhs whcl -> FunClause lhs (lowerMeta rhs) (lowerMeta whcl)
Data r ind n tel e1 cs -> Data r ind n
(lowerMeta tel) (lowerMeta e1) (lowerMeta cs)
SC.Record r n tel e1 cs -> SC.Record r n
(lowerMeta tel) (lowerMeta e1) (lowerMeta cs)
Infix _ _ -> d
Mutual r ds -> Mutual r (lowerMeta ds)
Abstract r ds -> Abstract r (lowerMeta ds)
Private r ds -> Private r (lowerMeta ds)
Postulate r sigs -> Postulate r (lowerMeta sigs)
SC.Primitive r sigs -> SC.Primitive r (lowerMeta sigs)
SC.Open _ _ _ -> d
SC.Import _ _ _ _ _ -> d
SC.Pragma _ -> d
ModuleMacro r n tel e1 op dir -> ModuleMacro r n
(lowerMeta tel) (lowerMeta e1) op dir
SC.Module r qn tel ds -> SC.Module r qn (lowerMeta tel) (lowerMeta ds)
instance LowerMeta SC.WhereClause where
lowerMeta SC.NoWhere = SC.NoWhere
lowerMeta (SC.AnyWhere ds) = SC.AnyWhere $ lowerMeta ds
lowerMeta (SC.SomeWhere m ds) = SC.SomeWhere m $ lowerMeta ds
instance LowerMeta a => LowerMeta [a] where
lowerMeta as = List.map lowerMeta as
instance LowerMeta a => LowerMeta (Arg a) where
lowerMeta aa = fmap lowerMeta aa
instance LowerMeta a => LowerMeta (Named name a) where
lowerMeta aa = fmap lowerMeta aa
preMeta = SC.QuestionMark noRange Nothing
preUscore = SC.Underscore noRange Nothing
cmd_compute :: Bool
-> GoalCommand
cmd_compute ignore ii rng s = ioTCM $ do
e <- B.parseExprIn ii rng s
d <- B.withInteractionId ii $ do
let c = B.evalInCurrent e
v <- if ignore then ignoreAbstractMode c else c
prettyA v
display_info "*Normal Form*" (show d)
parseAndDoAtToplevel
:: (SA.Expr -> TCM SA.Expr)
-> String
-> String
-> IO ()
parseAndDoAtToplevel cmd title s = infoOnException $ do
e <- parse exprParser s
mTopLevel <- theTopLevel <$> readIORef theState
ioTCM $ display_info title =<<
case mTopLevel of
Nothing -> return "Error: First load the file."
Just topLevel -> do
setScope $ insideScope topLevel
showA =<< cmd =<< concreteToAbstract_ e
cmd_infer_toplevel
:: B.Rewrite
-> String
-> IO ()
cmd_infer_toplevel norm =
parseAndDoAtToplevel (B.typeInCurrent norm) "*Inferred Type*"
cmd_compute_toplevel :: Bool
-> String -> IO ()
cmd_compute_toplevel ignore =
parseAndDoAtToplevel (if ignore then ignoreAbstractMode . c else c)
"*Normal Form*"
where c = B.evalInCurrent
emacsStr s = go (show s) where
go ('\\':ns@(n:_))
| isDigit n = toEnum (read digits :: Int) : go rest
where (digits, rest) = span isDigit ns
go (c:s) = c : go s
go [] = []
tellEmacsToUpdateGoals :: IO ()
tellEmacsToUpdateGoals = do
is <- theInteractionPoints <$> readIORef theState
UTF8.putStrLn $ response $ L [A "agda2-annotate", format is]
where format = Q . L . List.map showNumIId
tellEmacsToReloadSyntaxInfo :: IO ()
tellEmacsToReloadSyntaxInfo = do
UTF8.putStrLn $ response $ L [A "agda2-highlight-reload"]
tellEmacsToUpdateGoals
tellEmacsToJumpToError :: Range -> IO ()
tellEmacsToJumpToError r = do
case rStart r of
Nothing -> return ()
Just (Pn { srcFile = "" }) -> return ()
Just (Pn { srcFile = f, posPos = p }) ->
UTF8.putStrLn $ response $
L [A "annotation-goto", Q $ L [A (show f), A ".", A (show p)]]
tellEmacsToReloadSyntaxInfo
showImplicitArgs :: Bool
-> IO ()
showImplicitArgs showImpl = ioTCM $ do
opts <- commandLineOptions
setCommandLineOptions (opts { optShowImplicit = showImpl })
toggleImplicitArgs :: IO ()
toggleImplicitArgs = ioTCM $ do
opts <- commandLineOptions
setCommandLineOptions (opts { optShowImplicit =
not $ optShowImplicit opts })
errorTitle :: String
errorTitle = "*Error*"
displayErrorAndExit :: Either TCErr (Range, String) -> IO a
displayErrorAndExit err = do
runTCM $ do
appendErrorToEmacsFile err'
display_info errorTitle =<< msg
tellEmacsToJumpToError rng
exitWith (ExitFailure 1)
where
err' = case err of
Left e -> e
Right (rng, msg) -> Exception rng msg
msg = case err of
Left e -> prettyError e
Right (rng, msg)
| rng == noRange -> return msg
| otherwise -> return (show rng ++ "\n" ++ msg)
rng = case err of
Left e -> getRange e
Right (rng, msg) -> rng
infoOnException m =
failOnException inform m `catchImpossible` \e ->
inform noRange (show e)
where inform rng msg = displayErrorAndExit (Right (rng, msg))