crucible-syntax-0.4.1: src/Lang/Crucible/Syntax/Atoms.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings#-}
-- | Atoms used by the Crucible CFG concrete syntax.
module Lang.Crucible.Syntax.Atoms
(
-- * The atom datatype
Atomic(..)
, atom
-- * Individual atoms
, AtomName(..)
, LabelName(..)
, RegName(..)
, FunName(..)
, GlobalName(..)
, Keyword(..)
) where
import Control.Applicative
import Data.Char
import Data.Functor
import Data.Ratio
import Data.Text (Text)
import qualified Data.Text as T
import Lang.Crucible.Syntax.SExpr
import Numeric
import qualified Prettyprinter as PP
import Text.Megaparsec as MP hiding (many, some)
import Text.Megaparsec.Char
-- | The name of an atom (non-keyword identifier)
newtype AtomName = AtomName Text deriving (Eq, Ord, PP.Pretty, Show)
-- | The name of a label (identifier followed by colon)
newtype LabelName = LabelName Text deriving (Eq, Ord, PP.Pretty, Show)
-- | The name of a register (dollar sign followed by identifier)
newtype RegName = RegName Text deriving (Eq, Ord, PP.Pretty, Show)
-- | The name of a function (at-sign followed by identifier)
newtype FunName = FunName Text deriving (Eq, Ord, PP.Pretty, Show)
-- | The name of a global variable (two dollar signs followed by identifier)
newtype GlobalName = GlobalName Text deriving (Eq, Ord, PP.Pretty, Show)
-- | Individual language keywords (reserved identifiers)
data Keyword = Defun | DefBlock | DefGlobal | Declare | Extern
| Registers
| Start
| SetGlobal
| SetRef | DropRef_
| Plus | Minus | Times | Div | Negate | Abs
| Just_ | Nothing_ | FromJust
| Inj | Proj
| AnyT | UnitT | BoolT | NatT | IntegerT | RealT | ComplexRealT | CharT | StringT
| BitvectorT | VectorT | SequenceT | FPT | FunT | MaybeT | VariantT | StructT | RefT
| Half_ | Float_ | Double_ | Quad_ | X86_80_ | DoubleDouble_
| Unicode_ | Char8_ | Char16_
| The
| Equalp | Integerp
| If
| Not_ | And_ | Or_ | Xor_
| Mod
| Lt | Le
| Show
| StringConcat_ | StringEmpty_ | StringLength_
| ToAny | FromAny
| VectorLit_ | VectorReplicate_ | VectorIsEmpty_ | VectorSize_
| VectorGetEntry_ | VectorSetEntry_ | VectorCons_
| MkStruct_ | GetField_ | SetField_
| SequenceNil_ | SequenceCons_ | SequenceAppend_
| SequenceIsNil_ | SequenceLength_
| SequenceHead_ | SequenceTail_ | SequenceUncons_
| Deref | Ref | EmptyRef
| Jump_ | Return_ | Branch_ | MaybeBranch_ | TailCall_ | Error_ | Output_ | Case
| Print_ | PrintLn_
| Let | Fresh
| Assert_ | Assume_
| SetRegister
| Funcall
| Breakpoint_
| BV | BVConcat_ | BVSelect_ | BVTrunc_
| BVZext_ | BVSext_ | BVNonzero_ | BoolToBV_
| BVCarry_ | BVSCarry_ | BVSBorrow_
| BVNot_ | BVAnd_ | BVOr_ | BVXor_ | BVShl_ | BVLshr_ | BVAshr_
| Sle | Slt | Sdiv | Smod | ZeroExt | SignExt
| RNE_ | RNA_ | RTP_ | RTN_ | RTZ_
| FPToUBV_ | FPToSBV_ | UBVToFP_ | SBVToFP_ | BinaryToFP_ | FPToBinary_
| FPToReal_ | RealToFP_
deriving (Eq, Ord)
keywords :: [(Text, Keyword)]
keywords =
-- function/block defintion
[ ("defun" , Defun)
, ("start" , Start)
, ("defblock", DefBlock)
, ("defglobal", DefGlobal)
, ("declare", Declare)
, ("extern", Extern)
, ("registers", Registers)
-- statements
, ("let", Let)
, ("set-global!", SetGlobal)
, ("set-ref!", SetRef)
, ("drop-ref!", DropRef_)
, ("fresh", Fresh)
, ("jump" , Jump_)
, ("case", Case)
, ("return" , Return_)
, ("branch" , Branch_)
, ("maybe-branch" , MaybeBranch_)
, ("tail-call" , TailCall_)
, ("error", Error_)
, ("output", Output_)
, ("print" , Print_)
, ("println" , PrintLn_)
, ("Ref", RefT)
, ("deref", Deref)
, ("ref", Ref)
, ("empty-ref", EmptyRef)
, ("set-register!", SetRegister)
, ("assert!", Assert_)
, ("assume!", Assume_)
, ("funcall", Funcall)
, ("breakpoint", Breakpoint_)
-- types
, ("Any" , AnyT)
, ("Unit" , UnitT)
, ("Bool" , BoolT)
, ("Nat" , NatT)
, ("Integer" , IntegerT)
, ("FP", FPT)
, ("Real" , RealT)
, ("ComplexReal" , ComplexRealT)
, ("Char" , CharT)
, ("String" , StringT)
, ("Bitvector" , BitvectorT)
, ("Vector", VectorT)
, ("Sequence", SequenceT)
, ("->", FunT)
, ("Maybe", MaybeT)
, ("Variant", VariantT)
, ("Struct", StructT)
-- string sorts
, ("Unicode", Unicode_)
, ("Char16", Char16_)
, ("Char8", Char8_)
-- floating-point variants
, ("Half", Half_)
, ("Float", Float_)
, ("Double", Double_)
, ("Quad", Quad_)
, ("X86_80", X86_80_)
, ("DoubleDouble", DoubleDouble_)
-- misc
, ("the" , The)
, ("equal?" , Equalp)
, ("if" , If)
-- ANY types
, ("to-any", ToAny)
, ("from-any", FromAny)
-- booleans
, ("not" , Not_)
, ("and" , And_)
, ("or" , Or_)
, ("xor" , Xor_)
-- arithmetic
, ("+" , Plus)
, ("-" , Minus)
, ("*" , Times)
, ("/" , Div)
, ("<" , Lt)
, ("<=" , Le)
, ("<=$" , Sle)
, ("<$" , Slt)
, ("/$" , Sdiv)
, ("smod", Smod)
, ("negate", Negate)
, ("abs", Abs)
, ("mod" , Mod)
, ("integer?" , Integerp)
-- Variants
, ("inj", Inj)
, ("proj", Proj)
-- Structs
, ("struct", MkStruct_)
, ("get-field", GetField_)
, ("set-field", SetField_)
-- Maybe
, ("just" , Just_)
, ("nothing" , Nothing_)
, ("from-just" , FromJust)
-- Vectors
, ("vector", VectorLit_)
, ("vector-replicate", VectorReplicate_)
, ("vector-empty?", VectorIsEmpty_)
, ("vector-size", VectorSize_)
, ("vector-get", VectorGetEntry_)
, ("vector-set", VectorSetEntry_)
, ("vector-cons", VectorCons_)
-- Sequences
, ("seq-nil", SequenceNil_)
, ("seq-cons", SequenceCons_)
, ("seq-append", SequenceAppend_)
, ("seq-nil?", SequenceIsNil_)
, ("seq-length", SequenceLength_)
, ("seq-head", SequenceHead_)
, ("seq-tail", SequenceTail_)
, ("seq-uncons", SequenceUncons_)
-- strings
, ("show", Show)
, ("string-concat", StringConcat_)
, ("string-empty", StringEmpty_)
, ("string-length", StringLength_)
-- bitvector
, ("bv", BV)
, ("bv-concat", BVConcat_)
, ("bv-select", BVSelect_)
, ("bv-trunc", BVTrunc_)
, ("zero-extend", BVZext_)
, ("sign-extend", BVSext_)
, ("bv-nonzero", BVNonzero_)
, ("bool-to-bv", BoolToBV_)
, ("bv-carry", BVCarry_)
, ("bv-scarry", BVSCarry_)
, ("bv-sborrow", BVSBorrow_)
, ("bv-not", BVNot_)
, ("bv-and", BVAnd_)
, ("bv-or", BVOr_)
, ("bv-xor", BVXor_)
, ("shl", BVShl_)
, ("lshr", BVLshr_)
, ("ashr", BVAshr_)
-- floating-point
, ("fp-to-ubv", FPToUBV_)
, ("fp-to-sbv", FPToSBV_)
, ("ubv-to-fp", UBVToFP_)
, ("sbv-to-fp", SBVToFP_)
, ("fp-to-binary", FPToBinary_)
, ("binary-to-fp", BinaryToFP_)
, ("real-to-fp", RealToFP_)
, ("fp-to-real", FPToReal_)
, ("rne" , RNE_)
, ("rna" , RNA_)
, ("rtp" , RTP_)
, ("rtn" , RTN_)
, ("rtz" , RTZ_)
]
instance Show Keyword where
show k = case [str | (str, k') <- keywords, k == k'] of
[] -> "UNKNOWN KW"
(s:_) -> T.unpack s
-- | The atoms of the language
data Atomic = Kw !Keyword -- ^ Keywords are all the built-in operators and expression formers
| Lbl !LabelName -- ^ Labels, but not the trailing colon
| At !AtomName -- ^ Atom names (which look like Scheme symbols)
| Rg !RegName -- ^ Registers, whose names have a leading single $
| Gl !GlobalName -- ^ Global variables, whose names have a leading double $$
| Fn !FunName -- ^ Function names, minus the leading @
| Int !Integer -- ^ Literal integers
| Rat !Rational -- ^ Literal rational numbers
| Bool !Bool -- ^ Literal Booleans
| StrLit !Text -- ^ Literal strings
deriving (Eq, Ord, Show)
instance IsAtom Atomic where
showAtom (Kw s) = T.pack (show s)
showAtom (Lbl (LabelName l)) = l <> ":"
showAtom (Rg (RegName r)) = "$" <> r
showAtom (Gl (GlobalName r)) = "$$" <> r
showAtom (At (AtomName a)) = a
showAtom (Fn (FunName a)) = "@" <> a
showAtom (Int i) = T.pack (show i)
showAtom (Rat r) = T.pack (show (numerator r) ++ "/" ++ show (denominator r))
showAtom (Bool b) = if b then "#t" else "#f"
showAtom (StrLit s) = T.pack $ show s
-- | Parse an atom
atom :: Parser Atomic
atom = try (Lbl . LabelName <$> identifier <* char ':')
<|> Fn . FunName <$> (char '@' *> identifier)
<|> (char '$' *> ((char '$' *> (Gl . GlobalName <$> identifier)) <|> Rg . RegName <$> identifier))
<|> try (mkNum <$> signedPrefixedNumber <*> ((Just <$> (try (char '/') *> prefixedNumber)) <|> pure Nothing))
<|> kwOrAtom
<|> char '#' *> ((char 't' <|> char 'T') $> Bool True <|> (char 'f' <|> char 'F') $> Bool False)
<|> char '"' *> (StrLit . T.pack <$> stringContents)
where
mkNum x Nothing = Int x
mkNum x (Just y) = Rat (x % y)
stringContents :: Parser [Char]
stringContents = (char '\\' *> ((:) <$> escapeChar <*> stringContents))
<|> (char '"' $> [])
<|> ((:) <$> satisfy (const True) <*> stringContents)
escapeChar :: Parser Char
escapeChar = (char '\\' *> pure '\\')
<|> (char '"' *> pure '"')
<|> (char 'n' *> pure '\n')
<|> (char 't' *> pure '\t')
<?> "valid escape character"
kwOrAtom :: Parser Atomic
kwOrAtom = do x <- identifier
return $ maybe (At (AtomName x)) Kw (lookup x keywords)
signedPrefixedNumber :: (Eq a, Num a) => Parser a
signedPrefixedNumber =
char '+' *> prefixedNumber <|>
char '-' *> (negate <$> prefixedNumber) <|>
prefixedNumber
prefixedNumber :: (Eq a, Num a) => Parser a
prefixedNumber = try (char '0' *> maybehex) <|> decimal
where decimal = fromInteger . read <$> some (satisfy isDigit <?> "decimal digit")
maybehex = char 'x' *> hex <|> return 0
hex = reading $ readHex <$> some (satisfy (\c -> isDigit c || elem c ("abcdefABCDEF" :: String)) <?> "hex digit")
reading p =
p >>=
\case
[(x, "")] -> pure x
_ -> empty