crucible-debug-0.1.0: src/Lang/Crucible/Debug/Response.hs
{-|
Copyright : (c) Galois, Inc. 2025
Maintainer : Langston Barrett <langston@galois.com>
-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Lang.Crucible.Debug.Response
( NotApplicable(..)
, UserError(..)
, ResponseExt
, ProofResult(..)
, Response(..)
) where
import Control.Exception (IOException)
import Data.Foldable qualified as Foldable
import Data.Kind (Type)
import Data.Sequence qualified as Seq
import Data.Text qualified as Text
import Data.Text (Text)
import Data.Void (Void, absurd)
import Lang.Crucible.Debug.Arg (ArgParseError)
import Lang.Crucible.Debug.Command.Base qualified as BCmd
import Lang.Crucible.Debug.Command (Command)
import Lang.Crucible.Debug.Command qualified as Cmd
import Lang.Crucible.Debug.Regex (MatchError)
import Lang.Crucible.Debug.Statement (ParseError)
import Lang.Crucible.Debug.Statement qualified as Stmt
import Lang.Crucible.Debug.Style (Styled)
import Prettyprinter qualified as PP
import Text.Megaparsec.Error qualified as MP
import What4.FunctionName (FunctionName)
import What4.FunctionName qualified as W4
import What4.ProgramLoc qualified as W4
-- | The command was not applicable in this state
data NotApplicable
= DoneSimulating
| NoCfg
| NoFrame
| NotDone
| NotYetSimulating
deriving Show
instance PP.Pretty NotApplicable where
pretty =
\case
DoneSimulating -> "The simulator is no longer running"
NoCfg -> "Not executing a CFG"
NoFrame -> "Not in a call frame"
NotDone -> "Execution is not finished"
NotYetSimulating -> "The simulator is not yet running"
-- | The command could not be completed successfully for some reason the user
-- should have anticipated.
data UserError cExt
= ArgError (Command cExt) (MatchError Text ArgParseError)
| BadArgNumber Text
| FnNotFound FunctionName [FunctionName]
| LoadParseError (MP.ParseErrorBundle Text Void)
| LoadSyntaxError (PP.Doc Void)
| NoHelp [Text]
| NoSuchArgument Int
| NotACfg W4.FunctionName
| NotApplicable NotApplicable
| SourceParseError ParseError
deriving Show
instance PP.Pretty cExt => PP.Pretty (UserError cExt) where
pretty =
\case
ArgError cmd e ->
PP.vsep
[ "Bad arguments for command" PP.<+> PP.pretty cmd PP.<> ":"
, PP.pretty e
]
BadArgNumber txt ->
"Bad argument number:" PP.<+> PP.pretty txt
FnNotFound nm nms ->
PP.vcat
[ "No such function:" PP.<+> PP.pretty nm
, if null nms
then mempty
else
PP.vcat
[ "Known functions:"
, PP.vcat (map (PP.pretty . W4.functionName) nms)
]
]
LoadParseError err -> PP.pretty (MP.errorBundlePretty err)
LoadSyntaxError err -> fmap absurd err
NoHelp args -> "Can't help with" PP.<+> PP.pretty (Text.intercalate " " args)
NoSuchArgument i -> "No such argument:" PP.<+> PP.pretty i
NotACfg fNm -> "Not a CFG:" PP.<+> PP.pretty (W4.functionName fNm)
NotApplicable err -> PP.pretty err
SourceParseError err ->
"Parse error during" PP.<+>
PP.pretty (Cmd.Base @Void BCmd.Source) PP.<>
":" PP.<+>
PP.pretty (Stmt.renderParseError err)
type ResponseExt :: Type -> Type
type family ResponseExt cExt :: Type
type instance ResponseExt Void = Void
data ProofResult
= Proved
| Disproved
| Unknown
deriving Show
instance PP.Pretty ProofResult where
pretty =
\case
Proved -> "proved"
Disproved -> "disproved"
Unknown -> "unknown"
data Response cExt
= Arg (PP.Doc Void)
| Backtrace (PP.Doc Void)
| Block (PP.Doc Void)
| Cfg (PP.Doc Void)
| Clear Int
| Complete (PP.Doc Void)
| Delete W4.FunctionName Bool
| Frame (PP.Doc Void)
| Help (PP.Doc Void)
| IOError IOException
| Load FilePath Int
| Location W4.ProgramLoc
| Obligation [PP.Doc Void]
| PathCondition (PP.Doc Void)
| Prove (Seq.Seq (PP.Doc Void, ProofResult))
| ProveTimeout
| Ok
| Style [Styled]
| Trace [PP.Doc Void]
| Usage (PP.Doc Void)
| UserError (UserError cExt)
| XResponse (ResponseExt cExt)
deriving instance (Show (ResponseExt cExt), Show cExt) => Show (Response cExt)
instance (PP.Pretty (ResponseExt cExt), PP.Pretty cExt) => PP.Pretty (Response cExt) where
pretty =
\case
Arg a -> fmap absurd a
Backtrace bt -> fmap absurd bt
Block b -> fmap absurd b
Cfg c -> fmap absurd c
Complete c -> fmap absurd c
Clear 0 -> "No proof obligations to clear"
Clear 1 -> "Cleared 1 proof obligation"
Clear n -> "Cleared" PP.<+> PP.pretty n PP.<+> "proof obligations"
Delete nm True -> "Deleted breakpoint at" PP.<+> PP.pretty (W4.functionName nm)
Delete nm False -> "No breakpoint at" PP.<+> PP.pretty (W4.functionName nm)
Frame f -> fmap absurd f
Help h -> fmap absurd h
Style styles -> PP.hsep (map PP.pretty styles)
Load p i -> PP.hsep ["Loaded", PP.pretty i, "CFGs from", PP.pretty p]
IOError e -> PP.viaShow e
Location l -> PP.pretty (W4.plFunction l) PP.<> PP.viaShow (W4.plSourceLoc l)
Obligation obls ->
if null obls
then "No proof obligations"
else PP.vsep (map (fmap absurd) obls)
PathCondition p -> fmap absurd p
Prove results ->
if Seq.null results
then "No obligations to prove"
else
PP.vcat $
map
(\(goal, result) -> PP.vcat [fmap absurd goal, PP.pretty result])
(Foldable.toList results)
ProveTimeout -> "Proof timed out!"
Ok -> "Ok"
Trace t -> PP.vcat (PP.punctuate PP.line (map (fmap absurd) t))
Usage u -> fmap absurd u
UserError e -> PP.pretty e
XResponse rExt -> PP.pretty rExt