module DDC.Llvm.Syntax.Exp ( -- * Expressions Exp (..) , typeOfExp , isXVar, isXLit, isXUndef , isClosedConstantExp -- * Variables , Var (..) , nameOfVar , typeOfVar -- * Names , Name (..) -- * Literals , Lit (..) , typeOfLit , makeLitString) where import DDC.Llvm.Syntax.Type import DDC.Llvm.Syntax.Prim import DDC.Llvm.Pretty.Prim () import Data.Text (Text) import Data.Char import Numeric import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.ByteString as BS -- Exp ------------------------------------------------------------------------ -- | Expressions can be used directly as arguments to instructions. -- -- The expressions marked (synthetic) are safe conversions that do not -- branch or access memory. In the real LLVM syntax we cannot represent -- them as expressions, but they are flattened out to instructions by the -- Clean transform. -- data Exp -- | Use of a variable. = XVar Var -- | A literal. | XLit Lit -- | An undefined value. | XUndef Type -- | (synthetic) Cast an expression to the given type. | XConv Type Conv Exp -- | (synthetic) Get a pointer to an element of the expression. | XGet Type Exp [Exp] deriving (Eq, Show) -- | Take the type of an expression. typeOfExp :: Exp -> Type typeOfExp xx = case xx of XVar var -> typeOfVar var XLit lit -> typeOfLit lit XUndef t -> t XConv t _ _ -> t XGet t _ _ -> t -- | Check if this expression is an `XVar`. isXVar :: Exp -> Bool isXVar xx = case xx of XVar{} -> True _ -> False -- | Check if this expression is an `XLit`. isXLit :: Exp -> Bool isXLit xx = case xx of XLit{} -> True _ -> False -- | Check if this expression is an `XUndef`. isXUndef :: Exp -> Bool isXUndef xx = case xx of XUndef{} -> True _ -> False -- | Check whether this expression is closed, -- meaning it doesn't contain any variables that refer to the context. isClosedConstantExp :: Exp -> Bool isClosedConstantExp xx = case xx of XVar{} -> False XLit{} -> True XUndef{} -> True XConv _ _ x -> isClosedConstantExp x XGet _ x1 xs -> isClosedConstantExp x1 && all isClosedConstantExp xs -- Var ------------------------------------------------------------------------ -- | A variable that can be assigned to. data Var = Var Name Type deriving (Eq, Show) -- | Yield the name of a var. nameOfVar :: Var -> Name nameOfVar (Var n _) = n -- | Yield the type of a var. typeOfVar :: Var -> Type typeOfVar (Var _ t) = t instance Ord Var where compare (Var n1 _) (Var n2 _) = compare n1 n2 -- Name ----------------------------------------------------------------------- -- | Names of variables. data Name = NameGlobal String | NameLocal String deriving (Show, Eq, Ord) -- Lit ------------------------------------------------------------------------ -- | Literal data. data Lit -- | An integer literal = LitInt Type Integer -- | A floating-point literal. | LitFloat Type Double -- | A string literal. -- In LLVM these have the same type as array literals, but have a -- special syntax. The first component is the literal source text, -- while the second its the pretty printed hex encoding that -- the LLVM frontend accepts. | LitString { litSource :: Text , litHexEncoded :: Text , litEncodingLength :: Int } -- | A null pointer literal. -- Only applicable to pointer types | LitNull Type -- | A completely undefined value. | LitUndef Type deriving (Eq, Show) -- | Yield the `Type` of a `Lit`. typeOfLit :: Lit -> Type typeOfLit ll = case ll of LitInt t _ -> t LitFloat t _ -> t LitNull t -> t LitUndef t -> t LitString _ _ encLen -> TArray (fromIntegral encLen) (TInt 8) -- | Make a literal string from some text. makeLitString :: Text -> Lit makeLitString tx = let (txEnc, nEncLen) = encodeText (tx `T.append` (T.pack [chr 0])) in LitString tx txEnc nEncLen -- | Hex encode non-printable characters in this string. -- The LLVM frontend doesn't appear to be unicode-clean, so only unoffensive -- ASCII characters are printed verbatim. Everything is hex-encoded as UTF-8. encodeText :: Text -> (Text, Int) encodeText tx = go [] 0 tx where go accStr accLen xx = case T.uncons xx of Nothing -> (T.concat $ reverse accStr, accLen) Just (x, xs) -> let (str, len) = encodeChar x in go (str : accStr) (accLen + len) xs encodeChar c | c == ' ' || (isAscii c && isAlphaNum c) || (isAscii c && isPunctuation c && c /= '"') = (T.pack [c], 1) | otherwise = let bs = TE.encodeUtf8 $ T.pack [c] len = BS.length bs in ( T.pack $ concatMap (\b -> "\\" ++ (padL $ showHex b "")) $ BS.unpack bs , len) padL x | length x == 0 = "00" | length x == 1 = "0" ++ x | otherwise = x