packages feed

language-smtlib-0.2.0.0: src/Language/SMTLIB/Parser/Command.hs

-- | Parsers for commands, options and info flags.
module Language.SMTLIB.Parser.Command
  ( pCommand
  , pCommandLenient
  , pScript
  , pScriptLenient
  , pOption
  , pInfoFlag
  ) where

import Data.Functor (($>))
import qualified Data.Text as T
import Text.Megaparsec

import Language.SMTLIB.Parser.Internal
import Language.SMTLIB.Parser.Term
import Language.SMTLIB.Syntax.Annotation (SrcSpan)
import Language.SMTLIB.Syntax.Command

-- | A whole script: zero or more commands.  (Leading whitespace is the caller's
-- responsibility; see "Language.SMTLIB.Parser".)
pScript :: P (Script SrcSpan)
pScript = many pCommand

-- | Like 'pScript', but unrecognized commands are kept as
-- 'Language.SMTLIB.Syntax.Command.UnknownCommand' rather than rejected.
pScriptLenient :: P (Script SrcSpan)
pScriptLenient = many pCommandLenient

-- | A top-level @command@.
--
-- The command keyword is read once as a single word and then dispatched on,
-- rather than attempting each alternative in turn.  This avoids re-scanning the
-- keyword (and the backtracking that goes with it) for every command — a large
-- saving on scripts dominated by a few command shapes (e.g. @assert@).
--
-- An unrecognized head keyword is a parse error.  Use 'pCommandLenient' to keep
-- such commands as 'Language.SMTLIB.Syntax.Command.UnknownCommand' instead.
pCommand :: P (Command SrcSpan)
pCommand = pCommandWith $ \kw -> fail ("unknown command: " ++ T.unpack kw)

-- | Like 'pCommand', but an unrecognized head keyword is accepted: the command
-- is kept as 'Language.SMTLIB.Syntax.Command.UnknownCommand' carrying the
-- keyword and its raw argument s-expressions, leaving the application to decide
-- what to do with it.
--
-- The fallback fires /only/ on an unknown head keyword.  A recognized command
-- with malformed arguments (e.g. @(assert)@) still fails, so genuine syntax
-- errors in known commands are not silently swallowed.
pCommandLenient :: P (Command SrcSpan)
pCommandLenient = pCommandWith $ \kw -> UnknownCommand kw <$> many pSExpr

-- | The shared command body, parameterized over how to handle an unrecognized
-- head keyword.  The handler is given the keyword and yields a constructor
-- still awaiting its final 'SrcSpan' field (as 'withSpan' expects).
pCommandWith
  :: (T.Text -> P (SrcSpan -> Command SrcSpan))
  -> P (Command SrcSpan)
pCommandWith onUnknown = withSpan $ do
  _ <- openP
  kw <- pAnyWord
  c <- case kw of
    "set-logic"               -> SetLogic <$> pSymbolRaw
    "set-option"              -> SetOption <$> pOption
    "set-info"                -> SetInfo <$> pAttribute
    "declare-sort-parameter"  -> DeclareSortParameter <$> pSymbolRaw
    "declare-sort"            -> DeclareSort <$> pSymbolRaw <*> numeral
    "declare-const"           -> DeclareConst <$> pSymbolRaw <*> pSort
    "define-const"            -> DefineConst <$> pSymbolRaw <*> pSort <*> pTerm
    "declare-datatypes"       -> DeclareDatatypes <$> parens (some pSortDec)
                                                  <*> parens (some pDatatypeDec)
    "declare-datatype"        -> DeclareDatatype <$> pSymbolRaw <*> pDatatypeDec
    "declare-fun"             -> DeclareFun <$> pSymbolRaw <*> parens (many pSort) <*> pSort
    "define-sort"             -> DefineSort <$> pSymbolRaw <*> parens (many pSymbolRaw) <*> pSort
    "define-fun-rec"          -> DefineFunRec <$> pFunctionDef
    "define-funs-rec"         -> DefineFunsRec <$> parens (some pFunctionDec)
                                              <*> parens (some pTerm)
    "define-fun"              -> DefineFun <$> pFunctionDef
    "push"                    -> Push <$> (numeral <|> pure 1)
    "pop"                     -> Pop <$> (numeral <|> pure 1)
    "reset-assertions"        -> pure ResetAssertions
    "reset"                   -> pure Reset
    "assert"                  -> Assert <$> pTerm
    "check-sat-assuming"      -> CheckSatAssuming <$> parens (many pTerm)
    "check-sat"               -> pure CheckSat
    "get-assertions"          -> pure GetAssertions
    "get-assignment"          -> pure GetAssignment
    "get-info"                -> GetInfo <$> pInfoFlag
    "get-model"               -> pure GetModel
    "get-option"              -> GetOption <$> pKeyword
    "get-proof"               -> pure GetProof
    "get-unsat-assumptions"   -> pure GetUnsatAssumptions
    "get-unsat-core"          -> pure GetUnsatCore
    "get-value"               -> GetValue <$> parens (some pTerm)
    "echo"                    -> Echo <$> pStringLit
    "exit"                    -> pure Exit
    _                         -> onUnknown kw
  _ <- closeP
  pure c

-- | An @option@ for @set-option@.
pOption :: P (Option SrcSpan)
pOption = withSpan $ choice
  [ tok ":diagnostic-output-channel"    *> (DiagnosticOutputChannel <$> pStringLit)
  , tok ":global-declarations"          *> (GlobalDeclarations <$> pBool)
  , tok ":interactive-mode"             *> (InteractiveMode <$> pBool)
  , tok ":print-success"                *> (PrintSuccess <$> pBool)
  , tok ":produce-assertions"           *> (ProduceAssertions <$> pBool)
  , tok ":produce-assignments"          *> (ProduceAssignments <$> pBool)
  , tok ":produce-models"               *> (ProduceModels <$> pBool)
  , tok ":produce-proofs"               *> (ProduceProofs <$> pBool)
  , tok ":produce-unsat-assumptions"    *> (ProduceUnsatAssumptions <$> pBool)
  , tok ":produce-unsat-cores"          *> (ProduceUnsatCores <$> pBool)
  , tok ":random-seed"                  *> (RandomSeed <$> numeral)
  , tok ":regular-output-channel"       *> (RegularOutputChannel <$> pStringLit)
  , tok ":reproducible-resource-limit"  *> (ReproducibleResourceLimit <$> numeral)
  , tok ":verbosity"                    *> (Verbosity <$> numeral)
  , OptionAttribute <$> pAttribute
  ]

-- | An @info_flag@ for @get-info@.
pInfoFlag :: P (InfoFlag SrcSpan)
pInfoFlag = withSpan $ choice
  [ tok ":all-statistics"         $> AllStatistics
  , tok ":assertion-stack-levels" $> AssertionStackLevels
  , tok ":authors"                $> Authors
  , tok ":error-behavior"         $> ErrorBehaviorFlag
  , tok ":name"                   $> InfoName
  , tok ":reason-unknown"         $> ReasonUnknownFlag
  , tok ":version"                $> InfoVersion
  , InfoFlagKeyword <$> pKeyword
  ]