grisette-0.9.0.0: src/Grisette/Internal/Core/Data/SExpr.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Unused LANGUAGE pragma" #-}
-- |
-- Module : Grisette.Internal.Core.Data.SExpr
-- Copyright : (c) Sirui Lu 2024
-- License : BSD-3-Clause (see the LICENSE file)
--
-- Maintainer : siruilu@cs.washington.edu
-- Stability : Experimental
-- Portability : GHC only
module Grisette.Internal.Core.Data.SExpr
( SExpr (..),
showsSExprWithParens,
parseFileLocation,
fileLocation,
)
where
import Control.DeepSeq (NFData)
import qualified Data.Binary as Binary
import Data.Bytes.Serial (Serial (deserialize, serialize))
import Data.Hashable (Hashable)
import qualified Data.Serialize as Cereal
import Data.Serialize.Text ()
import qualified Data.Text as T
import Debug.Trace.LocationTH (__LOCATION__)
import GHC.Generics (Generic)
import Language.Haskell.TH.Syntax (Lift, unsafeTExpCoerce)
import Language.Haskell.TH.Syntax.Compat (SpliceQ, liftSplice)
-- | S-expression data type. Used for symbol metadata.
data SExpr = Atom T.Text | List [SExpr] | NumberAtom Integer | BoolAtom Bool
deriving stock (Eq, Ord, Generic, Lift)
deriving anyclass (Hashable, NFData, Serial)
instance Cereal.Serialize SExpr where
put = serialize
get = deserialize
instance Binary.Binary SExpr where
put = serialize
get = deserialize
instance Show SExpr where
showsPrec _ = showsSExprWithParens '(' ')'
unwordsS :: [ShowS] -> ShowS
unwordsS [] = id
unwordsS [x] = x
unwordsS (x : xs) = x . showString " " . unwordsS xs
-- | Show an S-expression with specific parentheses.
showsSExprWithParens :: Char -> Char -> SExpr -> ShowS
showsSExprWithParens _ _ (Atom s) = showString $ T.unpack s
showsSExprWithParens lp rp (List l) =
showString [lp] . unwordsS (map shows l) . (showString [rp])
showsSExprWithParens _ _ (NumberAtom n) = shows n
showsSExprWithParens _ _ (BoolAtom b) = showString $ if b then "#t" else "#f"
-- | Parse a file location string into an S-expression.
parseFileLocation :: String -> SExpr
parseFileLocation str =
let r = reverse str
(s2, r1) = break (== '-') r
(s1, r2) = break (== ':') $ tail r1
(l, p) = break (== ':') $ tail r2
in List
[ Atom "grisette-file-location",
Atom $ T.pack $ reverse $ tail p,
NumberAtom $ read $ reverse l,
List
[ NumberAtom $ read $ reverse s1,
NumberAtom $ read $ reverse s2
]
]
-- | Get the file location of the splice.
fileLocation :: SpliceQ SExpr
fileLocation =
[||parseFileLocation $$(liftSplice $ unsafeTExpCoerce __LOCATION__)||]