tock-mirror/common/EvalLiterals.hs
Neil Brown 8f767ff0d4 Made all the imports of Data.Generics have an import list
This makes sure that we catch all leftover instances of using SYB to do generic operations that we should be using Polyplate for instead.  Most modules should only import Data, and possibly Typeable.
2009-04-09 15:36:37 +00:00

202 lines
6.8 KiB
Haskell

{-
Tock: a compiler for parallel languages
Copyright (C) 2007 University of Kent
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
-}
-- | Evaluate simple literal expressions.
module EvalLiterals where
import Control.Monad.Error
import Control.Monad.Identity
import Control.Monad.State
import Data.Char
import Data.Generics (Data, Typeable)
import Data.Int
import Data.Maybe
import Data.Word
import Numeric
import qualified AST as A
import CompState hiding (CSM) -- everything here is read-only
import Errors
import Metadata
import Traversal
import TypeSizes
type EvalM = ErrorT ErrorReport (StateT CompState Identity)
instance Die EvalM where
dieReport = throwError
-- | Evaluated values of various types.
data OccValue =
OccBool Bool
| OccByte Word8
| OccUInt16 Word16
| OccUInt32 Word32
| OccUInt64 Word64
| OccInt8 Int8
| OccInt16 Int16
| OccInt CIntReplacement
| OccInt32 Int32
| OccInt64 Int64
| OccReal32 Float
| OccReal64 Double
| OccArray [OccValue]
| OccRecord A.Name [OccValue]
deriving (Show, Eq, Typeable, Data)
-- | Is an expression a constant literal?
isConstant :: A.Expression -> Bool
isConstant (A.Literal _ _ (A.ArrayListLiteral _ aes))
= isConstantStruct aes
isConstant (A.Literal _ _ (A.RecordLiteral _ es))
= and $ map isConstant es
isConstant (A.Literal _ _ _) = True
isConstant (A.True _) = True
isConstant (A.False _) = True
isConstant _ = False
-- | Is an array literal element constant?
isConstantStruct :: A.Structured A.Expression -> Bool
isConstantStruct (A.Several _ ss) = and $ map isConstantStruct ss
isConstantStruct (A.Only _ e) = isConstant e
isConstantStruct (A.ProcThen {}) = False
isConstantStruct (A.Spec {}) = False
-- | Evaluate a byte literal.
evalByte :: (CSMR m, Die m) => Meta -> String -> m Char
evalByte m s
= do ps <- getCompState
case runEvaluator ps (evalByteLiteral m OccByte s) of
Left (m', err) ->
dieReport (m', "Cannot evaluate byte literal: " ++ err)
Right (OccByte ch) ->
return (chr $ fromIntegral ch)
-- | Run an evaluator operation.
runEvaluator :: CompState -> EvalM OccValue -> Either ErrorReport OccValue
runEvaluator ps func
= runIdentity (evalStateT (runErrorT func) ps)
-- | Evaluate a simple literal expression.
evalSimpleExpression :: A.Expression -> EvalM OccValue
evalSimpleExpression e@(A.Literal _ _ _) = evalSimpleLiteral e
evalSimpleExpression e = throwError (Just $ findMeta e, "Not a literal")
-- | Turn the result of one of the read* functions into an OccValue,
-- or throw an error if it didn't parse.
fromRead :: Meta -> (a -> OccValue) -> (String -> [(a, String)])
-> String -> EvalM OccValue
fromRead m cons reader s
= case reader s of
[(v, "")] -> return $ cons v
_ -> throwError (Just m, "Cannot parse literal: " ++ s)
-- | Evaluate a simple (non-array) literal.
evalSimpleLiteral :: A.Expression -> EvalM OccValue
evalSimpleLiteral (A.Literal m t lr)
= underlyingType m t >>= \t' -> case t' of
A.Infer -> defaults
A.Byte -> into OccByte
A.UInt16 -> into OccUInt16
A.UInt32 -> into OccUInt32
A.UInt64 -> into OccUInt64
A.Int8 -> into OccInt8
A.Int16 -> into OccInt16
A.Int -> into OccInt
A.Int32 -> into OccInt32
A.Int64 -> into OccInt64
A.Real32 -> intoF OccReal32
A.Real64 -> intoF OccReal64
_ -> bad
where
defaults :: EvalM OccValue
defaults
= case lr of
A.ByteLiteral _ s -> evalByteLiteral m OccByte s
A.IntLiteral _ s -> fromRead m OccInt (readSigned readDec) s
A.HexLiteral _ s -> fromRead m OccInt readHex s
A.RealLiteral _ s -> fromRead m OccReal32 readFloat' s
_ -> bad
into :: (Num t, Real t) => (t -> OccValue) -> EvalM OccValue
into cons
= case lr of
A.ByteLiteral _ s -> evalByteLiteral m cons s
A.IntLiteral _ s -> fromRead m cons (readSigned readDec) s
A.HexLiteral _ s -> fromRead m cons readHex s
_ -> bad
intoF :: RealFrac t => (t -> OccValue) -> EvalM OccValue
intoF cons
= case lr of
A.ByteLiteral _ s -> evalByteLiteral m cons s
A.IntLiteral _ s -> fromRead m cons (readSigned readDec) s
A.HexLiteral _ s -> fromRead m cons readHex s
A.RealLiteral _ s -> fromRead m cons readFloat' s
_ -> bad
-- readFloat only handles unsigned values, so we need to look out for the negation
-- ourselves:
readFloat' :: RealFrac a => ReadS a
readFloat' [] = []
readFloat' ('-':rest) = [(negate x, s) | (x, s) <- readFloat rest]
readFloat' s = readFloat s
bad :: EvalM OccValue
bad = throwError (Just m, "Cannot evaluate literal")
m = findMeta lr
-- | Evaluate a byte literal.
evalByteLiteral :: Num t => Meta -> (t -> OccValue) -> String -> EvalM OccValue
evalByteLiteral m cons ('*':'#':hex)
= do OccInt n <- fromRead m OccInt readHex hex
return $ cons (fromIntegral n)
evalByteLiteral _ cons ['*', ch]
= return $ cons (fromIntegral $ ord $ star ch)
where
star :: Char -> Char
star 'c' = '\r'
star 'n' = '\n'
star 't' = '\t'
star 's' = ' '
star c = c
evalByteLiteral _ cons [ch]
= return $ cons (fromIntegral $ ord ch)
evalByteLiteral m _ _ = throwError (Just m, "Bad BYTE literal")
-- | Resolve a datatype into its underlying type -- i.e. if it's a named data
-- type, then return the underlying real type. This will recurse.
underlyingType :: forall m. (CSMR m, Die m) => Meta -> A.Type -> m A.Type
underlyingType m = applyTopDownM (resolveUserType m)
-- After resolving a user type, we have to recurse
-- on the resulting type, so we must use a top-down transformation.
-- | Like underlyingType, but only do the "outer layer": if you give this a
-- user type that's an array of user types, then you'll get back an array of
-- user types.
resolveUserType :: (CSMR m, Die m) => Meta -> A.Type -> m A.Type
resolveUserType m (A.UserDataType n)
= do st <- specTypeOfName n
case st of
A.DataType _ t -> resolveUserType m t
_ -> dieP m $ "Not a type name: " ++ show n
resolveUserType _ t = return t