{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module What4.Symbol
( SolverSymbol
, solverSymbolAsText
, SolverSymbolError
, emptySymbol
, userSymbol
, systemSymbol
, safeSymbol
, ppSolverSymbolError
) where
import Data.Char
import Data.Hashable
import Data.Set (Set)
import qualified Data.Set as Set
import Data.String
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Text.Encoding.Z as Z
isAsciiLetter :: Char -> Bool
isAsciiLetter c
= 'A' <= c && c <= 'Z'
|| 'a' <= c && c <= 'z'
isSymbolChar :: Char -> Bool
isSymbolChar c
= isAsciiLetter c
|| isDigit c
|| c == '_'
|| c == '\''
|| c == '!'
data SolverSymbolError
= SymbolEmpty
| SymbolNoStartWithChar
| SymbolIllegalChar
| SymbolSMTLIBReserved
| SymbolYicesReserved
instance Show SolverSymbolError where
show e = "Identifier " ++ ppSolverSymbolError e
ppSolverSymbolError :: SolverSymbolError -> String
ppSolverSymbolError e =
case e of
SymbolEmpty -> "cannot be empty."
SymbolNoStartWithChar -> "must start with a letter."
SymbolIllegalChar -> "contains an illegal character."
SymbolSMTLIBReserved -> "is an SMTLIB reserved word."
SymbolYicesReserved -> "is a Yices reserved word."
newtype SolverSymbol = SolverSymbol { solverSymbolAsText :: Text }
deriving (Eq, Ord, Hashable)
emptySymbol :: SolverSymbol
emptySymbol = SolverSymbol Text.empty
userSymbol :: String -> Either SolverSymbolError SolverSymbol
userSymbol s
| elem '!' s = Left SymbolIllegalChar
| otherwise = parseAnySymbol s
systemSymbol :: String -> SolverSymbol
systemSymbol s
| '!' `notElem` s =
error $
"The system symbol " ++ show s ++ " must contain at least one exclamation mark '!'"
| otherwise =
case parseAnySymbol s of
Left e -> error ("Error parsing system symbol " ++ show s ++ ": " ++ ppSolverSymbolError e)
Right r -> r
safeSymbol :: String -> SolverSymbol
safeSymbol str =
case userSymbol str of
Right s -> s
Left _err -> systemSymbol ("zenc!" ++ Z.zEncodeString str)
instance Show SolverSymbol where
show s = Text.unpack (solverSymbolAsText s)
parseAnySymbol :: String -> Either SolverSymbolError SolverSymbol
parseAnySymbol [] = Right emptySymbol
parseAnySymbol (h:r)
| isAsciiLetter h == False = Left SymbolNoStartWithChar
| all isSymbolChar r == False = Left SymbolIllegalChar
| t `Set.member` smtlibKeywordSet = Left SymbolSMTLIBReserved
| t `Set.member` yicesKeywordSet = Left SymbolYicesReserved
| otherwise = Right (SolverSymbol t)
where t = if elem '\'' r
then fromString ("|" ++ (h:r) ++ "|")
else fromString (h:r)
smtlibKeywordSet :: Set Text
smtlibKeywordSet = Set.fromList (fromString <$> smtlibKeywords)
yicesKeywordSet :: Set Text
yicesKeywordSet = Set.fromList (fromString <$> yicesKeywords)
smtlibKeywords :: [String]
smtlibKeywords =
[ "BINARY"
, "DECIMAL"
, "HEXADECIMAL"
, "NUMERAL"
, "STRING"
, "as"
, "let"
, "exists"
, "forall"
, "par"
, "assert"
, "check-sat"
, "check-sat-assuming"
, "declare-const"
, "declare-fun"
, "declare-sort"
, "define-fun"
, "define-fun-rec"
, "define-funs-rec"
, "define-sort"
, "echo"
, "exit"
, "get-assertions"
, "get-assignment"
, "get-info"
, "get-model"
, "get-option"
, "get-proof"
, "get-unsat-assumptions"
, "get-unsat-core"
, "get-value"
, "pop"
, "push"
, "reset"
, "reset-assertions"
, "set-info"
, "set-logic"
, "set-option"
]
yicesKeywords :: [String]
yicesKeywords =
[ "abs"
, "and"
, "assert"
, "bit"
, "bitvector"
, "bool"
, "bool-to-bv"
, "bv-add"
, "bv-and"
, "bv-ashift-right"
, "bv-ashr"
, "bv-comp"
, "bv-concat"
, "bv-div"
, "bv-extract"
, "bv-ge"
, "bv-gt"
, "bv-le"
, "bv-lshr"
, "bv-lt"
, "bv-mul"
, "bv-nand"
, "bv-neg"
, "bv-nor"
, "bv-not"
, "bv-or"
, "bv-pow"
, "bv-redand"
, "bv-redor"
, "bv-rem"
, "bv-repeat"
, "bv-rotate-left"
, "bv-rotate-right"
, "bv-sdiv"
, "bv-sge"
, "bv-sgt"
, "bv-shift-left0"
, "bv-shift-left1"
, "bv-shift-right0"
, "bv-shift-right1"
, "bv-shl"
, "bv-sign-extend"
, "bv-sle"
, "bv-slt"
, "bv-smod"
, "bv-srem"
, "bv-sub"
, "bv-xnor"
, "bv-xor"
, "bv-zero-extend"
, "ceil"
, "check"
, "define"
, "define-type"
, "distinct"
, "div"
, "divides"
, "dump-context"
, "echo"
, "ef-solve"
, "eval"
, "exists"
, "exit"
, "export-to-dimacs"
, "false"
, "floor"
, "forall"
, "help"
, "if"
, "include"
, "int"
, "is-int"
, "ite"
, "lambda"
, "let"
, "mk-bv"
, "mk-tuple"
, "mod"
, "not"
, "or"
, "pop"
, "push"
, "real"
, "reset"
, "reset-stats"
, "scalar"
, "select"
, "set-param"
, "set-timeout"
, "show-implicant"
, "show-model"
, "show-param"
, "show-params"
, "show-stats"
, "true"
, "tuple"
, "tuple-update"
, "update"
, "xor"
]