Added a signed 8-bit type and unsigned 16-,32- and 64-bit types to the AST, adjusting all other code accordingly

The types have been added to the AST.  Beyond the obvious trivial changes (extra cases in functions, etc), the only
significant change was that isSafeConversion needed to be changed.  I took the opportunity to totally rewrite the 
function into a graph-like mechanism rather than just using a list.  To demonstrate its correctness I also wrote an
exhaustive test for it.
This commit is contained in:
Neil Brown 2007-08-21 12:43:00 +00:00
parent 3b14eec036
commit 96f6bc39fd
7 changed files with 190 additions and 20 deletions

29
AST.hs
View File

@ -90,8 +90,26 @@ data ChanAttributes = ChanAttributes {
-- have.
data Type =
Bool
-- | 8-bit unsigned integer.
| Byte
| Int | Int16 | Int32 | Int64
-- | 16-bit unsigned integer. Only exists in Rain.
| UInt16
-- | 32-bit unsigned integer. Only exists in Rain.
| UInt32
-- | 64-bit unsigned integer. Only exists in Rain.
| UInt64
-- | 8-bit signed integer. Only exists in Rain.
| Int8
-- | In occam: a signed integer that uses the most efficient word-size in the target. In Rain: transformed to an Int64.
| Int
-- | 16-bit signed integer.
| Int16
-- | 32-bit signed integer.
| Int32
-- | 64-bit signed integer.
| Int64
| Real32 | Real64
-- | An array.
-- For N-dimensional arrays, the [Dimension] list will be of length N.
@ -113,7 +131,14 @@ data Type =
instance Show Type where
show Bool = "BOOL"
show Byte = "BYTE"
show Byte = "BYTE"
--Not sure how to show the non-occam types -- just use their AST names:
show UInt16 = "UInt16"
show UInt32 = "UInt32"
show UInt64 = "UInt64"
show Int8 = "Int8"
show Int = "INT"
show Int16 = "INT16"
show Int32 = "INT32"

View File

@ -122,6 +122,14 @@ evalExpression (A.Dyadic _ op e1 e2)
evalDyadic op v1 v2
evalExpression (A.MostPos _ A.Byte) = return $ OccByte maxBound
evalExpression (A.MostNeg _ A.Byte) = return $ OccByte minBound
evalExpression (A.MostPos _ A.UInt16) = return $ OccUInt16 maxBound
evalExpression (A.MostNeg _ A.UInt16) = return $ OccUInt16 minBound
evalExpression (A.MostPos _ A.UInt32) = return $ OccUInt32 maxBound
evalExpression (A.MostNeg _ A.UInt32) = return $ OccUInt32 minBound
evalExpression (A.MostPos _ A.UInt64) = return $ OccUInt64 maxBound
evalExpression (A.MostNeg _ A.UInt64) = return $ OccUInt64 minBound
evalExpression (A.MostPos _ A.Int8) = return $ OccInt8 maxBound
evalExpression (A.MostNeg _ A.Int8) = return $ OccInt8 minBound
evalExpression (A.MostPos _ A.Int) = return $ OccInt maxBound
evalExpression (A.MostNeg _ A.Int) = return $ OccInt minBound
evalExpression (A.MostPos _ A.Int16) = return $ OccInt16 maxBound
@ -163,6 +171,10 @@ evalExpression e = throwError "bad expression"
evalMonadicOp :: (forall t. (Num t, Integral t, Bits t) => t -> t) -> OccValue -> EvalM OccValue
evalMonadicOp f (OccByte a) = return $ OccByte (f a)
evalMonadicOp f (OccUInt16 a) = return $ OccUInt16 (f a)
evalMonadicOp f (OccUInt32 a) = return $ OccUInt32 (f a)
evalMonadicOp f (OccUInt64 a) = return $ OccUInt64 (f a)
evalMonadicOp f (OccInt8 a) = return $ OccInt8 (f a)
evalMonadicOp f (OccInt a) = return $ OccInt (f a)
evalMonadicOp f (OccInt16 a) = return $ OccInt16 (f a)
evalMonadicOp f (OccInt32 a) = return $ OccInt32 (f a)
@ -179,6 +191,10 @@ evalMonadic _ _ = throwError "bad monadic op"
evalDyadicOp :: (forall t. (Num t, Integral t, Bits t) => t -> t -> t) -> OccValue -> OccValue -> EvalM OccValue
evalDyadicOp f (OccByte a) (OccByte b) = return $ OccByte (f a b)
evalDyadicOp f (OccUInt16 a) (OccUInt16 b) = return $ OccUInt16 (f a b)
evalDyadicOp f (OccUInt32 a) (OccUInt32 b) = return $ OccUInt32 (f a b)
evalDyadicOp f (OccUInt64 a) (OccUInt64 b) = return $ OccUInt64 (f a b)
evalDyadicOp f (OccInt8 a) (OccInt8 b) = return $ OccInt8 (f a b)
evalDyadicOp f (OccInt a) (OccInt b) = return $ OccInt (f a b)
evalDyadicOp f (OccInt16 a) (OccInt16 b) = return $ OccInt16 (f a b)
evalDyadicOp f (OccInt32 a) (OccInt32 b) = return $ OccInt32 (f a b)
@ -187,6 +203,10 @@ evalDyadicOp _ _ _ = throwError "dyadic operator not implemented for this type"
evalCompareOp :: (forall t. (Eq t, Ord t) => t -> t -> Bool) -> OccValue -> OccValue -> EvalM OccValue
evalCompareOp f (OccByte a) (OccByte b) = return $ OccBool (f a b)
evalCompareOp f (OccUInt16 a) (OccUInt16 b) = return $ OccBool (f a b)
evalCompareOp f (OccUInt32 a) (OccUInt32 b) = return $ OccBool (f a b)
evalCompareOp f (OccUInt64 a) (OccUInt64 b) = return $ OccBool (f a b)
evalCompareOp f (OccInt8 a) (OccInt8 b) = return $ OccBool (f a b)
evalCompareOp f (OccInt a) (OccInt b) = return $ OccBool (f a b)
evalCompareOp f (OccInt16 a) (OccInt16 b) = return $ OccBool (f a b)
evalCompareOp f (OccInt32 a) (OccInt32 b) = return $ OccBool (f a b)
@ -233,6 +253,10 @@ renderValue m v = (t, A.Literal m t lr)
renderLiteral :: Meta -> OccValue -> (A.Type, A.LiteralRepr)
renderLiteral m (OccByte c) = (A.Byte, A.ByteLiteral m $ renderChar (chr $ fromIntegral c))
renderLiteral m (OccUInt16 i) = (A.UInt16, A.IntLiteral m $ show i)
renderLiteral m (OccUInt32 i) = (A.UInt32, A.IntLiteral m $ show i)
renderLiteral m (OccUInt64 i) = (A.UInt64, A.IntLiteral m $ show i)
renderLiteral m (OccInt8 i) = (A.Int8, A.IntLiteral m $ show i)
renderLiteral m (OccInt i) = (A.Int, A.IntLiteral m $ show i)
renderLiteral m (OccInt16 i) = (A.Int16, A.IntLiteral m $ show i)
renderLiteral m (OccInt32 i) = (A.Int32, A.IntLiteral m $ show i)

View File

@ -43,6 +43,12 @@ instance Die EvalM where
data OccValue =
OccBool Bool
| OccByte Word8
-- The following four aren't occam types, but I need to put them in here for handling Rain code:
| OccUInt16 Word16
| OccUInt32 Word32
| OccUInt64 Word64
| OccInt8 Int8
| OccInt Int32
| OccInt16 Int16
| OccInt32 Int32
@ -112,6 +118,22 @@ evalSimpleLiteral (A.Literal _ A.Byte (A.IntLiteral _ s))
= fromRead OccByte (readSigned readDec) s
evalSimpleLiteral (A.Literal _ A.Byte (A.HexLiteral _ s))
= fromRead OccByte readHex s
evalSimpleLiteral (A.Literal _ A.UInt16 (A.IntLiteral _ s))
= fromRead OccUInt16 (readSigned readDec) s
evalSimpleLiteral (A.Literal _ A.UInt16 (A.HexLiteral _ s))
= fromRead OccUInt16 readHex s
evalSimpleLiteral (A.Literal _ A.UInt32 (A.IntLiteral _ s))
= fromRead OccUInt32 (readSigned readDec) s
evalSimpleLiteral (A.Literal _ A.UInt32 (A.HexLiteral _ s))
= fromRead OccUInt32 readHex s
evalSimpleLiteral (A.Literal _ A.UInt64 (A.IntLiteral _ s))
= fromRead OccUInt64 (readSigned readDec) s
evalSimpleLiteral (A.Literal _ A.UInt64 (A.HexLiteral _ s))
= fromRead OccUInt64 readHex s
evalSimpleLiteral (A.Literal _ A.Int8 (A.IntLiteral _ s))
= fromRead OccInt8 (readSigned readDec) s
evalSimpleLiteral (A.Literal _ A.Int8 (A.HexLiteral _ s))
= fromRead OccInt8 readHex s
evalSimpleLiteral (A.Literal _ A.Int (A.IntLiteral _ s))
= fromRead OccInt (readSigned readDec) s
evalSimpleLiteral (A.Literal _ A.Int (A.HexLiteral _ s))

View File

@ -318,6 +318,10 @@ genName n = tell [nameString n]
cgetScalarType :: GenOps -> A.Type -> Maybe String
cgetScalarType _ A.Bool = Just "bool"
cgetScalarType _ A.Byte = Just "uint8_t"
cgetScalarType _ A.UInt16 = Just "uint16_t"
cgetScalarType _ A.UInt32 = Just "uint32_t"
cgetScalarType _ A.UInt64 = Just "uint64_t"
cgetScalarType _ A.Int8 = Just "int8_t"
cgetScalarType _ A.Int = Just "int"
cgetScalarType _ A.Int16 = Just "int16_t"
cgetScalarType _ A.Int32 = Just "int32_t"

View File

@ -961,6 +961,10 @@ cppgenSizeSuffix _ dim = tell [".extent(", dim, ")"]
cppgetScalarType :: GenOps -> A.Type -> Maybe String
cppgetScalarType _ A.Bool = Just "tockBool"
cppgetScalarType _ A.Byte = Just "uint8_t"
cppgetScalarType _ A.UInt16 = Just "uint16_t"
cppgetScalarType _ A.UInt32 = Just "uint32_t"
cppgetScalarType _ A.UInt64 = Just "uint64_t"
cppgetScalarType _ A.Int8 = Just "int8_t"
cppgetScalarType _ A.Int = Just "int"
cppgetScalarType _ A.Int16 = Just "int16_t"
cppgetScalarType _ A.Int32 = Just "int32_t"

View File

@ -140,6 +140,66 @@ testFunctionsToProcs2 = testPassWithItemsStateCheck "testFunctionsToProcs2 A" ex
assertEqual "testFunctionsToProcs2 F" (Just [A.Int]) (Map.lookup "foo" (csFunctionReturns state))
assertEqual "testFunctionsToProcs2 G" (Just [A.Int]) (Map.lookup "fooOuter" (csFunctionReturns state))
--Not strictly a pass test but it can live here for now:
testIsSafeConversion :: Test
testIsSafeConversion = TestList $ map runTestRow resultsWithIndexes
where
resultsWithIndexes :: [(Int,[(Int,Bool)])]
resultsWithIndexes = zip [0..] $ map (zip [0..]) results
runTestRow :: (Int,[(Int,Bool)]) -> Test
runTestRow (a,b) = TestList $ map (runTest a) b
where
runTest :: Int -> (Int,Bool) -> Test
runTest destIndex (srcIndex,result) = TestCase $ assertEqual
("Testing from type: " ++ (show $ index srcIndex) ++ " to: " ++ (show $ index destIndex))
result $ isSafeConversion (index srcIndex) (index destIndex)
--Integer types are:
--A.Bool
--A.Byte
--A.UInt16
--A.UInt32
--A.UInt64
--A.Int8
--A.Int
--A.Int16
--A.Int32
--A.Int64
--We will assume (like the rest of Tock) that Int is 32-bits for testing. We can actually perform an exhaustive test without too much trouble:
index :: Int -> A.Type
index 0 = A.Bool
index 1 = A.Byte
index 2 = A.UInt16
index 3 = A.UInt32
index 4 = A.UInt64
index 5 = A.Int8
index 6 = A.Int16
index 7 = A.Int
index 8 = A.Int32
index 9 = A.Int64
t = True
f = False
results :: [[Bool]]
--Each row is a conversion to that type. For example, the first row is conversions *to* Bool:
results =
[ [t, f,f,f,f, f,f,f,f,f] --to Bool
,[t, t,f,f,f, f,f,f,f,f] --to Byte
,[t, t,t,f,f, f,f,f,f,f] --to UInt16
,[t, t,t,t,f, f,f,f,f,f] --to UInt32
,[t, t,t,t,t, f,f,f,f,f] --to UInt64
,[t, f,f,f,f, t,f,f,f,f] --to Int8
,[t, t,f,f,f, t,t,f,f,f] --to Int16
,[t, t,t,f,f, t,t,t,t,f] --to Int
,[t, t,t,f,f, t,t,t,t,f] --to Int32
,[t, t,t,t,f, t,t,t,t,t] --to Int64
]
--Returns the list of tests:
@ -149,6 +209,7 @@ tests = TestList
testFunctionsToProcs0
,testFunctionsToProcs1
,testFunctionsToProcs2
,testIsSafeConversion
]

View File

@ -303,30 +303,52 @@ isPreciseConversion fromT toT
= fromT == toT || not (isRealType fromT || isRealType toT)
-- | Will a conversion between two types always succeed?
--Parameters are src dest
isSafeConversion :: A.Type -> A.Type -> Bool
isSafeConversion A.Real32 A.Real64 = True
isSafeConversion fromT toT = (fromT == toT) || ((fromP /= -1) && (toP /= -1) && (fromP <= toP))
isSafeConversion src dest = (src' == dest') || ((src' == A.Bool || isIntegerType src') && (dest' == A.Bool || isIntegerType dest') && (findCastRoute dest' src'))
where
fromP = precNum fromT
toP = precNum toT
src' = convInt src
dest' = convInt dest
precNum :: A.Type -> Int
precNum t = precNum' t 0 convPrec
--Turn Int into Int32:
convInt :: A.Type -> A.Type
convInt A.Int = A.Int32
convInt t = t
precNum' :: A.Type -> Int -> [[A.Type]] -> Int
precNum' _ n [] = (-1)
precNum' t n (tl:tls)
= if t `elem` tl then n
else precNum' t (n + 1) tls
--Parameters are dest src
findCastRoute :: A.Type -> A.Type -> Bool
findCastRoute dest src
--Either a direct converstion is possible
= (elem (dest,src) possibleConversions)
--Or there exists some chained conversion:
|| (any (findCastRoute dest) (findDests src possibleConversions))
convPrec :: [[A.Type]]
convPrec
= [ [A.Bool]
, [A.Byte]
, [A.Int16]
, [A.Int, A.Int32]
, [A.Int64]
]
--Finds all the conversions from the src type using the given list of (dest,src)
--Note that the list must not allow any cycles! (or else we will engage in infinite recursion)
findDests :: A.Type -> [(A.Type,A.Type)] -> [A.Type]
findDests _ [] = []
findDests src ((dest,src'):ts) = if src == src' then dest : (findDests src ts) else findDests src ts
--Listed in order (dest, src)
--Signed numbers cannot be safely cast to unsigned numbers. So (A.UInt16, A.Int8) isn't possible
possibleConversions :: [(A.Type,A.Type)]
possibleConversions
= [
(A.Byte, A.Bool)
,(A.Int8, A.Bool)
,(A.Int16, A.Int8)
,(A.Int16, A.Byte)
,(A.Int32, A.Int16)
,(A.Int32, A.UInt16)
,(A.Int64, A.Int32)
,(A.Int64, A.UInt32)
,(A.UInt16, A.Byte)
,(A.UInt32, A.UInt16)
,(A.UInt64, A.UInt32)
]
--{{{ classes of types
-- | Scalar integer types.
@ -334,6 +356,10 @@ isIntegerType :: A.Type -> Bool
isIntegerType t
= case t of
A.Byte -> True
A.UInt16 -> True
A.UInt32 -> True
A.UInt64 -> True
A.Int8 -> True
A.Int -> True
A.Int16 -> True
A.Int32 -> True
@ -389,6 +415,10 @@ data BytesInResult =
-- | Return the size in bytes of a data type.
bytesInType :: (CSM m, Die m) => A.Type -> m BytesInResult
bytesInType A.Byte = return $ BIJust 1
bytesInType A.UInt16 = return $ BIJust 2
bytesInType A.UInt32 = return $ BIJust 4
bytesInType A.UInt64 = return $ BIJust 8
bytesInType A.Int8 = return $ BIJust 1
-- FIXME This is tied to the backend we're using (as is the constant folder).
bytesInType A.Int = return $ BIJust 4
bytesInType A.Int16 = return $ BIJust 2