packages feed

language-smtlib-0.1.0.0: src/Language/SMTLIB/Syntax/Constant.hs

-- | Lexical constants of the SMT-LIB 2 language: symbols, keywords, indices and
-- spec-constants, plus helpers to interpret the numeric literals.
--
-- 'Symbol', 'Keyword' and the string payload of 'SCString' always hold the
-- /logical/ value (unquoted, unescaped).  Quoting and escaping is entirely the
-- printer's responsibility.  The numeric literals 'SCDecimal', 'SCHexadecimal'
-- and 'SCBinary' keep the raw lexeme 'Text' so that printing round-trips
-- byte-for-byte (leading zeros, letter case); use the interpreters below to
-- recover their values.
module Language.SMTLIB.Syntax.Constant
  ( Symbol
  , Keyword
  , SpecConstant(..)
  , Index(..)
    -- * Numeric interpreters
  , hexToInteger
  , binToInteger
  , decimalToScientific
  ) where

import Data.Char (digitToInt)
import Data.Scientific (Scientific)
import Data.Text (Text)
import qualified Data.Text as T

import Language.SMTLIB.Syntax.Annotation (Annotated(..))

-- | An SMT-LIB symbol, stored as its logical (unquoted) value.
type Symbol = Text

-- | An SMT-LIB keyword, stored /without/ its leading colon.
type Keyword = Text

-- | A @spec_constant@.
data SpecConstant a
  = SCNumeral     !Integer a  -- ^ @42@
  | SCDecimal     !Text    a  -- ^ raw lexeme, e.g. @"1.500"@
  | SCHexadecimal !Text    a  -- ^ digits only (no @#x@), e.g. @"00aF"@
  | SCBinary      !Text    a  -- ^ digits only (no @#b@), e.g. @"0110"@
  | SCString      !Text    a  -- ^ decoded string value (no quotes, @""@ unescaped)
  deriving (Show, Eq, Functor, Foldable, Traversable)

-- | An @index@ of an indexed identifier @(_ sym i ...)@.
data Index a
  = IxNumeral !Integer a
  | IxSymbol  !Symbol  a
  deriving (Show, Eq, Functor, Foldable, Traversable)

instance Annotated SpecConstant where
  ann = \case
    SCNumeral _ a     -> a
    SCDecimal _ a     -> a
    SCHexadecimal _ a -> a
    SCBinary _ a      -> a
    SCString _ a      -> a
  setAnn a = \case
    SCNumeral x _     -> SCNumeral x a
    SCDecimal x _     -> SCDecimal x a
    SCHexadecimal x _ -> SCHexadecimal x a
    SCBinary x _      -> SCBinary x a
    SCString x _      -> SCString x a

instance Annotated Index where
  ann = \case
    IxNumeral _ a -> a
    IxSymbol _ a  -> a
  setAnn a = \case
    IxNumeral x _ -> IxNumeral x a
    IxSymbol x _  -> IxSymbol x a

-- | Interpret the digits of an 'SCHexadecimal' lexeme as an 'Integer'.
hexToInteger :: Text -> Integer
hexToInteger = T.foldl' (\acc c -> acc * 16 + toInteger (digitToInt c)) 0

-- | Interpret the digits of an 'SCBinary' lexeme as an 'Integer'.
binToInteger :: Text -> Integer
binToInteger = T.foldl' (\acc c -> acc * 2 + toInteger (digitToInt c)) 0

-- | Interpret an 'SCDecimal' lexeme as a 'Scientific' value.
decimalToScientific :: Text -> Scientific
decimalToScientific = read . T.unpack