module Language.C.Syntax.Constants (
escapeChar, unescapeChar, unescapeString,
Flags(..), noFlags, setFlag, clearFlag, testFlag,
cChar, cChar_w, cChars, CChar(..), getCChar, getCCharAsInt, isWideChar, showCharConst,
CIntFlag(..), CIntRepr(..), cInteger, CInteger(..), getCInteger,readCInteger,
cFloat, CFloat(..), readCFloat,
cString, cString_w, CString(..), getCString, showStringLit, concatCStrings,
)
where
import Data.Bits
import Data.Char
import Numeric (showOct, showHex, readHex, readOct, readDec)
import Language.C.Data.Node
import Language.C.Data.Position
import Data.Generics
data CChar = CChar
!Char
!Bool
| CChars
[Char]
!Bool
deriving (Eq,Ord,Data,Typeable)
instance Show CChar where
showsPrec _ (CChar c wideflag) = _showWideFlag wideflag . showCharConst c
showsPrec _ (CChars cs wideflag) = _showWideFlag wideflag . (sQuote $ concatMap escapeCChar cs)
showCharConst :: Char -> ShowS
showCharConst c = sQuote $ escapeCChar c
_showWideFlag :: Bool -> ShowS
_showWideFlag flag = if flag then showString "L" else id
getCChar :: CChar -> [Char]
getCChar (CChar c _) = [c]
getCChar (CChars cs _) = cs
getCCharAsInt :: CChar -> Integer
getCCharAsInt (CChar c _) = fromIntegral (fromEnum c)
getCCharAsInt (CChars _cs _) = error "integer value of multi-character character constants is implementation defined"
isWideChar :: CChar -> Bool
isWideChar (CChar _ wideFlag) = wideFlag
isWideChar (CChars _ wideFlag) = wideFlag
cChar :: Char -> CChar
cChar c = CChar c False
cChar_w :: Char -> CChar
cChar_w c = CChar c True
cChars :: [Char] -> Bool -> CChar
cChars = CChars
data CIntRepr = DecRepr | HexRepr | OctalRepr deriving (Eq,Ord,Enum,Bounded,Data,Typeable)
data CIntFlag = FlagUnsigned | FlagLong | FlagLongLong | FlagImag deriving (Eq,Ord,Enum,Bounded,Data,Typeable)
instance Show CIntFlag where
show FlagUnsigned = "u"
show FlagLong = "L"
show FlagLongLong = "LL"
show FlagImag = "i"
data CInteger = CInteger
!Integer
!CIntRepr
!(Flags CIntFlag)
deriving (Eq,Ord,Data,Typeable)
instance Show CInteger where
showsPrec _ (CInteger i repr flags) = showInt i . showString (concatMap showIFlag [FlagUnsigned .. ]) where
showIFlag f = if testFlag f flags then show f else []
showInt i = case repr of DecRepr -> shows i
OctalRepr -> showString "0" . showOct i
HexRepr -> showString "0x" . showHex i
readCInteger :: CIntRepr -> String -> Either String CInteger
readCInteger repr str =
case readNum str of
[(n,suffix)] -> mkCInt n suffix
parseFailed -> Left $ "Bad Integer literal: "++show parseFailed
where
readNum = case repr of DecRepr -> readDec; HexRepr -> readHex; OctalRepr -> readOct
mkCInt n suffix = either Left (Right . CInteger n repr) $ readSuffix suffix
readSuffix = parseFlags noFlags
parseFlags flags [] = Right flags
parseFlags flags ('l':'l':fs) = parseFlags (setFlag FlagLongLong flags) fs
parseFlags flags ('L':'L':fs) = parseFlags (setFlag FlagLongLong flags) fs
parseFlags flags (f:fs) =
let go1 flag = parseFlags (setFlag flag flags) fs in
case f of
'l' -> go1 FlagLong ; 'L' -> go1 FlagLong
'u' -> go1 FlagUnsigned ; 'U' -> go1 FlagUnsigned
'i' -> go1 FlagImag ; 'I' -> go1 FlagImag; 'j' -> go1 FlagImag; 'J' -> go1 FlagImag
_ -> Left $ "Unexpected flag " ++ show f
getCInteger :: CInteger -> Integer
getCInteger (CInteger i _ _) = i
cInteger :: Integer -> CInteger
cInteger i = CInteger i DecRepr noFlags
data CFloat = CFloat
!String
deriving (Eq,Ord,Data,Typeable)
instance Show CFloat where
showsPrec _ (CFloat internal) = showString internal
cFloat :: Float -> CFloat
cFloat = CFloat . show
readCFloat :: String -> CFloat
readCFloat = CFloat
data CString = CString
[Char]
Bool
deriving (Eq,Ord,Data,Typeable)
instance Show CString where
showsPrec _ (CString str wideflag) = _showWideFlag wideflag . showStringLit str
cString :: String -> CString
cString str = CString str False
cString_w :: String -> CString
cString_w str = CString str True
getCString :: CString -> String
getCString (CString str _) = str
isWideString :: CString -> Bool
isWideString (CString _ wideflag) = wideflag
concatCStrings :: [CString] -> CString
concatCStrings cs = CString (concatMap getCString cs) (any isWideString cs)
showStringLit :: String -> ShowS
showStringLit = dQuote . concatMap showStringChar
where
showStringChar c | isSChar c = return c
| c == '"' = "\\\""
| otherwise = escapeChar c
isAsciiSourceChar :: Char -> Bool
isAsciiSourceChar c = isAscii c && isPrint c
isCChar :: Char -> Bool
isCChar '\\' = False
isCChar '\'' = False
isCChar '\n' = False
isCChar c = isAsciiSourceChar c
escapeCChar :: Char -> String
escapeCChar '\'' = "\\'"
escapeCChar c | isCChar c = [c]
| otherwise = escapeChar c
isSChar :: Char -> Bool
isSChar '\\' = False
isSChar '\"' = False
isSChar '\n' = False
isSChar c = isAsciiSourceChar c
showOct' :: Int -> String
showOct' i = replicate (3 length s) '0' ++ s
where s = showOct i ""
escapeChar :: Char -> String
escapeChar '\\' = "\\\\"
escapeChar '\a' = "\\a"
escapeChar '\b' = "\\b"
escapeChar '\ESC' = "\\e";
escapeChar '\f' = "\\f"
escapeChar '\n' = "\\n"
escapeChar '\r' = "\\r"
escapeChar '\t' = "\\t"
escapeChar '\v' = "\\v"
escapeChar c | (ord c) < 512 = '\\' : showOct' (ord c)
| otherwise = '\\' : 'x' : showHex (ord c) ""
unescapeChar :: String -> (Char, String)
unescapeChar ('\\':c:cs) = case c of
'n' -> ('\n', cs)
't' -> ('\t', cs)
'v' -> ('\v', cs)
'b' -> ('\b', cs)
'r' -> ('\r', cs)
'f' -> ('\f', cs)
'a' -> ('\a', cs)
'e' -> ('\ESC', cs)
'E' -> ('\ESC', cs)
'\\' -> ('\\', cs)
'?' -> ('?', cs)
'\'' -> ('\'', cs)
'"' -> ('"', cs)
'x' -> case head' "bad escape sequence" (readHex cs) of
(i, cs') -> (toEnum i, cs')
_ -> case head' "bad escape sequence" (readOct' (c:cs)) of
(i, cs') -> (toEnum i, cs')
unescapeChar (c :cs) = (c, cs)
unescapeChar [] = error $ "unescape char: empty string"
readOct' :: ReadS Int
readOct' s = map (\(i, cs) -> (i, cs ++ rest)) (readOct octStr)
where octStr = takeWhile isOctDigit $ take 3 s
rest = drop (length octStr) s
unescapeString :: String -> String
unescapeString [] = []
unescapeString cs = case unescapeChar cs of
(c, cs') -> c : unescapeString cs'
sQuote :: String -> ShowS
sQuote s t = "'" ++ s ++ "'" ++ t
dQuote :: String -> ShowS
dQuote s t = ('"' : s) ++ "\"" ++ t
head' :: String -> [a] -> a
head' err [] = error err
head' _ (x:_) = x
newtype Flags f = Flags Integer deriving (Eq,Ord,Data,Typeable)
noFlags :: Flags f
noFlags = Flags 0
setFlag :: (Enum f) => f -> Flags f -> Flags f
setFlag flag (Flags k) = Flags$ k `setBit` fromEnum flag
clearFlag :: (Enum f) => f -> Flags f -> Flags f
clearFlag flag (Flags k) = Flags$ k `clearBit` fromEnum flag
testFlag :: (Enum f) => f -> Flags f -> Bool
testFlag flag (Flags k) = k `testBit` fromEnum flag