packages feed

sbv-14.8: Data/SBV/Compilers/C/Types.hs

-----------------------------------------------------------------------------
-- |
-- Module    : Data.SBV.Compilers.C.Types
-- Copyright : (c) Levent Erkok
-- License   : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Shared C type and helper names for structurally lowered SBV kinds.
-----------------------------------------------------------------------------

{-# OPTIONS_GHC -Wall -Werror #-}

module Data.SBV.Compilers.C.Types
  ( tupleCType
  , adtCType
  , definedFunctionCName
  , arrayKindTag
  , arrayOutputCTypeName
  , arrayStoredCloneName
  , arrayStoredReleaseName
  , tupleFieldName
  , tupleOwnedInitName
  , tupleOwnedSetName
  , tupleOwnedCloneName
  , tupleOwnedReleaseName
  , adtOwnedInitName
  , adtOwnedSetName
  , adtOwnedCloneName
  , adtOwnedReleaseName
  , adtEqualName
  , listCloneName
  , listReleaseName
  , listHelperName
  , setCloneName
  , setReleaseName
  , setHelperName
  , textCloneName
  , textReleaseName
  , textCompareName
  , elementCType
  , constElementCType
  , kindTag
  , isConcreteADT
  , isConcreteADTReference
  ) where

import Data.Char                   (isAlphaNum, isAscii, ord)
import Numeric                     (showHex)

import Data.SBV.Compilers.C.FP         (arbitraryFPCType)
import Data.SBV.Core.Data

-- | Return the helper name that initializes caller-owned storage for a tuple
-- with recursively managed fields.
tupleOwnedInitName :: Kind -> String
tupleOwnedInitName kind = "sbv_tuple_owned_init_" ++ kindTag kind

-- | Return the helper name that assigns into initialized owned tuple storage.
tupleOwnedSetName :: Kind -> String
tupleOwnedSetName kind = "sbv_tuple_owned_set_" ++ kindTag kind

-- | Return the public helper name that deep-copies a managed-field tuple.
tupleOwnedCloneName :: Kind -> String
tupleOwnedCloneName kind = "sbv_tuple_owned_clone_" ++ kindTag kind

-- | Return the public helper name that releases an owned managed-field tuple.
tupleOwnedReleaseName :: Kind -> String
tupleOwnedReleaseName kind = "sbv_tuple_owned_release_" ++ kindTag kind

-- | Return the helper name that initializes caller-owned storage for one ADT
-- constructor.
adtOwnedInitName :: Kind -> String
adtOwnedInitName kind = "sbv_adt_owned_init_" ++ adtCType kind

-- | Return the helper name that assigns into initialized owned ADT storage.
adtOwnedSetName :: Kind -> String
adtOwnedSetName kind = "sbv_adt_owned_set_" ++ adtCType kind

-- | Return the public helper name that deep-copies an owned ADT.
adtOwnedCloneName :: Kind -> String
adtOwnedCloneName kind = "sbv_adt_owned_clone_" ++ adtCType kind

-- | Return the public helper name that releases an owned ADT.
adtOwnedReleaseName :: Kind -> String
adtOwnedReleaseName kind = "sbv_adt_owned_release_" ++ adtCType kind

-- | Return the generated structural-equality helper for a recursive ADT.
adtEqualName :: Bool -> Kind -> String
adtEqualName strong kind = "sbv_adt_" ++ (if strong then "object_" else "")
                        ++ "equal_" ++ adtCType kind

-- | Return the generated clone-helper name for a list kind.
listCloneName :: Kind -> String
listCloneName (KList elementKind) = "sbv_list_clone_" ++ kindTag elementKind
listCloneName kind                = error $ "SBV->C: Expected a list kind, received " ++ show kind

-- | Return the generated release-helper name for a list kind.
listReleaseName :: Kind -> String
listReleaseName (KList elementKind) = "sbv_list_release_" ++ kindTag elementKind
listReleaseName kind                = error $ "SBV->C: Expected a list kind, received " ++ show kind

-- | Return one specialized list-operation helper name.
listHelperName :: Kind -> String -> String
listHelperName (KList elementKind) suffix = "sbv_list_" ++ kindTag elementKind ++ "_" ++ suffix
listHelperName kind                _      = error $ "SBV->C: Expected a list kind, received " ++ show kind

-- | Return the generated clone-helper name for a set kind.
setCloneName :: Kind -> String
setCloneName (KSet elementKind) = "sbv_set_clone_" ++ kindTag elementKind
setCloneName kind               = error $ "SBV->C: Expected a set kind, received " ++ show kind

-- | Return the generated release-helper name for a set kind.
setReleaseName :: Kind -> String
setReleaseName (KSet elementKind) = "sbv_set_release_" ++ kindTag elementKind
setReleaseName kind               = error $ "SBV->C: Expected a set kind, received " ++ show kind

-- | Return one specialized set-operation helper name.
setHelperName :: Kind -> String -> String
setHelperName (KSet elementKind) suffix = "sbv_set_" ++ kindTag elementKind ++ "_" ++ suffix
setHelperName kind               _      = error $ "SBV->C: Expected a set kind, received " ++ show kind

-- | Name of the public helper that copies a string into owned storage.
textCloneName :: String
textCloneName = "sbv_string_clone"

-- | Name of the public helper that releases owned string storage.
textReleaseName :: String
textReleaseName = "sbv_string_release"

-- | Name of the lexicographic string-comparison runtime helper.
textCompareName :: String
textCompareName = "sbv_text_compare"

-- | A resolved user ADT, excluding built-in rounding modes and opaque sorts.
isConcreteADT :: Kind -> Bool
isConcreteADT kind = isADT kind && not (isRoundingMode kind) && not (isUninterpreted kind)

-- | A user ADT or a retained application awaiting ADT-reference resolution.
-- Keep this distinct from 'isConcreteADT' for dependency and ownership walks.
isConcreteADTReference :: Kind -> Bool
isConcreteADTReference KApp{} = True
isConcreteADTReference kind   = isConcreteADT kind

-- | Return the public C structure type used for a tuple kind.
tupleCType :: Kind -> String
tupleCType kind@KTuple{} = "SBVTuple_" ++ kindTag kind
tupleCType kind          = error $ "SBV->C: Expected a tuple kind, received " ++ show kind

-- | Return the public C structure type used for a concrete ADT kind or an
-- unresolved application of a registered ADT.
adtCType :: Kind -> String
adtCType kind@(KADT typeName parameters _)
  | isADT kind && not (isRoundingMode kind) && not (isUninterpreted kind)
  = appliedType typeName (map snd parameters)
  | True
  = error $ "SBV->C: Expected a concrete ADT kind, received " ++ show kind
adtCType (KApp typeName arguments) = appliedType typeName arguments
adtCType kind = error $ "SBV->C: Expected an ADT kind, received " ++ show kind

-- | Return the private C identifier used for an SBV-defined function. Encoding
-- the complete SMT identifier keeps quoted and firstified names collision-free.
definedFunctionCName :: String -> String
definedFunctionCName functionName = "sbv_function_" ++ encodeIdentifier functionName

-- | Render the common C type spelling shared by a concrete ADT and an
-- unresolved application of that same registered ADT.
appliedType :: String -> [Kind] -> String
appliedType typeName arguments = "SBVADT_" ++ encodeIdentifier typeName ++ concatMap parameterTag arguments
 where parameterTag parameterKind = "_" ++ show (length tag) ++ "_" ++ tag
        where tag = kindTag parameterKind

-- | Return the public member name used for a one-based tuple field index.
tupleFieldName :: Int -> String
tupleFieldName index
  | index >= 1 = "field" ++ show index
  | True       = error $ "SBV->C: Tuple fields are one-based, received " ++ show index

-- | Return the public C type used for a structurally lowered field or element.
elementCType :: Kind -> String
elementCType KBool               = "SBool"
elementCType (KBounded False 1)  = "SBool"
elementCType (KBounded False w)  = "SWord" ++ show w
elementCType (KBounded True  w)  = "SInt" ++ show w
elementCType KUnbounded          = "SInteger"
elementCType KReal               = "SReal"
elementCType KRational           = "SRational"
elementCType KFloat              = "SFloat"
elementCType KDouble             = "SDouble"
elementCType KChar               = "SChar"
elementCType KString             = "SString"
elementCType kind@KFP{}          = arbitraryFPCType kind
elementCType kind@KTuple{}       = tupleCType kind
elementCType kind@KADT{}
  | isRoundingMode kind          = "RoundingMode"
  | True                         = adtCType kind
elementCType kind@KApp{}         = adtCType kind
elementCType (KList elementKind) = "SBVList_" ++ kindTag elementKind
elementCType (KSet elementKind)  = "SBVSet_" ++ kindTag elementKind
elementCType kind@KArray{}       = arrayOutputCTypeName kind ++ " *"
elementCType kind                = error $ "SBV->C: Unsupported structural kind: " ++ show kind

-- | Qualify a stored value itself. Array values are spelled as pointers, so
-- their const qualifier belongs after the pointer, not on its pointee.
constElementCType :: Kind -> String
constElementCType kind
  | isArray kind = elementCType kind ++ " const"
  | True         = "const " ++ elementCType kind

-- | Return the suffix used by a generated structural C type. Structural
-- components are length-framed and identifiers are escaped injectively.
-- Consumers must preserve case, including in preprocessor guards and tags.
-- Booleans and unsigned one-bit vectors intentionally share a representation,
-- as do a concrete ADT and its corresponding resolved application.
kindTag :: Kind -> String
kindTag KBool               = "u1"
kindTag (KBounded False w)  = "u" ++ show w
kindTag (KBounded True  w)  = "s" ++ show w
kindTag KUnbounded          = "integer"
kindTag KReal               = "real"
kindTag KRational           = "rational"
kindTag KFloat              = "float"
kindTag KDouble             = "double"
kindTag KChar               = "char"
kindTag KString             = "string"
kindTag (KFP eb sb)         = "fp_e" ++ show eb ++ "_s" ++ show sb
kindTag (KTuple fields)     = "t" ++ show (length fields) ++ concatMap (('_' :) . taggedKind . kindTag) fields
kindTag kind@KADT{}
  | isRoundingMode kind     = "rounding_mode"
  | True                    = "adt_" ++ encodeIdentifier (adtCType kind)
kindTag kind@KApp{}         = "adt_" ++ encodeIdentifier (adtCType kind)
kindTag (KList elementKind) = "list_" ++ taggedKind (kindTag elementKind)
kindTag (KSet elementKind)  = "set_"  ++ taggedKind (kindTag elementKind)
kindTag kind@KArray{}       = "array_" ++ taggedKind (arrayKindTag kind)
kindTag kind                = error $ "SBV->C: Unsupported structural kind: " ++ show kind

-- | Return the key/value suffix shared by generated names for an array kind.
-- Frame both components by length, just as for tuple fields and ADT arguments,
-- so nested tags need not be parsed to find the key/value boundary.
--
-- >>> arrayKindTag (KArray (KBounded False 8) (KBounded False 32))
-- "2_u8_3_u32"
arrayKindTag :: Kind -> String
arrayKindTag (KArray keyKind valueKind) = taggedKind (kindTag keyKind) ++ "_" ++ taggedKind (kindTag valueKind)
arrayKindTag kind                       = error $ "SBV->C: Expected an array kind, received " ++ show kind

-- | Return the public owned-output descriptor name for an array kind.
arrayOutputCTypeName :: Kind -> String
arrayOutputCTypeName kind@KArray{} = "SBVArrayOutput_" ++ arrayKindTag kind
arrayOutputCTypeName kind          = error $ "SBV->C: Expected an array kind, received " ++ show kind

-- | Return the helper name that clones an array descriptor stored by pointer
-- inside another generated value.
arrayStoredCloneName :: Kind -> String
arrayStoredCloneName kind@KArray{} = "sbv_array_stored_clone_" ++ arrayKindTag kind
arrayStoredCloneName kind          = error $ "SBV->C: Expected an array kind, received " ++ show kind

-- | Return the helper name that releases an array descriptor stored by pointer
-- inside another generated value.
arrayStoredReleaseName :: Kind -> String
arrayStoredReleaseName kind@KArray{} = "sbv_array_stored_release_" ++ arrayKindTag kind
arrayStoredReleaseName kind          = error $ "SBV->C: Expected an array kind, received " ++ show kind

-- | Prefix a generated kind tag with its length so adjacent tags cannot
-- collide.
taggedKind :: String -> String
taggedKind value = show (length value) ++ "_" ++ value

-- | Encode an arbitrary Haskell type name as a valid C identifier component.
encodeIdentifier :: String -> String
encodeIdentifier = concatMap encode
 where encode character
         | isAscii character && isAlphaNum character = [character]
         | True                                      = "_x" ++ showHex (ord character) "" ++ "_"