libcspm-1.0.0: src/CSPM/Evaluator/Values.hs
module CSPM.Evaluator.Values (
Value(..), UProc, UProcOperator, Proc(..), CSPOperator(..),
ProcOperator(..), Event(..), EventSet,
ScopeIdentifier(..), FunctionIdentifier(..),
compareValues,
procName, scopeId, annonymousScopeId,
valueEventToEvent,
noSave, maybeSave, removeThunk, lookupVar,
tupleFromList,
trimValueForProcessName,
module Data.Array,
) where
import CSPM.DataStructures.Names
import CSPM.DataStructures.Syntax
import CSPM.DataStructures.Types
import CSPM.Evaluator.Monad
import CSPM.Evaluator.ProcessValues
import {-# SOURCE #-} qualified CSPM.Evaluator.ValueSet as S
import Data.Array
import qualified Data.Foldable as F
import Data.Hashable
import qualified Data.Map as M
import Prelude hiding (lookup)
import Util.Exception
import Util.Prelude
type UProc = UnCompiledProc
type UProcOperator = UnCompiledProcOperator
data Value =
VInt Int
| VChar Char
| VBool Bool
| VTuple (Array Int Value)
-- | If A is a datatype clause that has 3 fields a b c then a runtime
-- instantiation of this would be VDot [VDataType "A", a, b, c] where a,b
-- and c can contain other VDots.
| VDot [Value]
-- The following two never appear on their own, they are always part of a
-- VDot (even if the VDot has no values).
| VChannel Name
| VDataType Name
| VList [Value]
| VMap (M.Map Value Value)
| VSet S.ValueSet
| VFunction FunctionIdentifier ([Value] -> EvaluationMonad Value)
| VProc UProc
| VThunk (EvaluationMonad Value)
-- | A disambiguator between different occurences of either processes or
-- functions. This works by storing the values that are bound (i.e. the free
-- variables the inner `thing` may depend on). This is used as a 'ProcName' and
-- for 'FunctionIdentifier's.
data ScopeIdentifier =
SFunctionBind {
scopeFunctionName :: Name,
scopeFunctionArguments :: [[Value]],
parentScopeIdentifier :: Maybe ScopeIdentifier
}
| SVariableBind {
variablesBound :: [Value],
parentScopeIdentifier :: Maybe ScopeIdentifier
}
instance Eq ScopeIdentifier where
SFunctionBind n1 vss1 p1 == SFunctionBind n2 vss2 p2 =
n1 == n2 && vss1 == vss2 && p1 == p2
SVariableBind vs1 p1 == SVariableBind vs2 p2 =
vs1 == vs2 && p1 == p2
instance Hashable ScopeIdentifier where
hash (SFunctionBind n1 args1 p1) =
combine 1 (combine (hash n1) (combine (hash args1) (hash p1)))
hash (SVariableBind vs1 p1) = combine 2 (combine (hash vs1) (hash p1))
instance Ord ScopeIdentifier where
compare (SFunctionBind n1 vss1 p1) (SFunctionBind n2 vss2 p2) =
compare n1 n2 `thenCmp` compare vss1 vss2 `thenCmp` compare p1 p2
compare (SFunctionBind _ _ _) _ = LT
compare _ (SFunctionBind _ _ _) = GT
compare (SVariableBind vs1 p1) (SVariableBind vs2 p2) =
compare vs1 vs2 `thenCmp` compare p1 p2
data FunctionIdentifier =
FBuiltInFunction {
functionName :: Name,
arguments :: [Value]
}
| FLambda {
lambdaExpression :: Exp Name,
parentFunctionIdentifier :: Maybe ScopeIdentifier
}
| FMatchBind {
functionName :: Name,
argumentGroups :: [[Value]],
-- | The free variables this is bound in
scopeIdentifier :: Maybe ScopeIdentifier
}
instance Eq FunctionIdentifier where
FBuiltInFunction n1 vs1 == FBuiltInFunction n2 vs2 =
n1 == n2 && vs1 == vs2
FLambda e1 parent1 == FLambda e2 parent2 =
e1 == e2 && parent1 == parent2
FMatchBind n1 args1 parent1 == FMatchBind n2 args2 parent2 =
n1 == n2 && args1 == args2 && parent1 == parent2
_ == _ = False
instance Hashable FunctionIdentifier where
hash (FBuiltInFunction n1 vs) = combine 2 (combine (hash n1) (hash vs))
hash (FLambda expr parent) =
combine 3 (combine (hash (show expr)) (hash parent))
hash (FMatchBind n vs parent) =
combine 4 (combine (hash n) (combine (hash vs) (hash parent)))
instance Ord FunctionIdentifier where
compare (FBuiltInFunction n1 args1) (FBuiltInFunction n2 args2) =
compare n1 n2 `thenCmp` compare args1 args2
compare (FBuiltInFunction _ _) _ = LT
compare _ (FBuiltInFunction _ _) = GT
compare (FLambda e1 parent1) (FLambda e2 parent2) =
compare parent1 parent2 `thenCmp` compare e1 e2
compare (FLambda _ _) _ = LT
compare _ (FLambda _ _) = GT
compare (FMatchBind n1 vs1 parent1) (FMatchBind n2 vs2 parent2) =
compare n1 n2 `thenCmp` compare parent1 parent2 `thenCmp` compare vs1 vs2
tupleFromList :: [Value] -> Value
tupleFromList vs = VTuple $! listArray (0, length vs - 1) vs
-- | Given a program that yields a value, returns a second program that can be
-- inserted into the environment, but will cause the environment not to save
-- the actual value, but to recompute it everytime. This is useful for cheap,
-- to compute, but high cost in terms of memory, computations (like named
-- processes).
noSave :: EvaluationMonad Value -> EvaluationMonad Value
noSave prog = do
pn <- getParentScopeIdentifier
tok <- gets timedSection
return $ VThunk $ modify (\ st -> st {
CSPM.Evaluator.Monad.parentScopeIdentifier = pn,
timedSection = tok }) prog
maybeSave :: Type -> EvaluationMonad Value -> EvaluationMonad Value
maybeSave TProc prog = noSave prog
maybeSave _ prog = prog
removeThunk :: Value -> EvaluationMonad Value
removeThunk (VThunk p) = p
removeThunk v = return v
lookupVar :: Name -> EvaluationMonad Value
lookupVar n = lookupVarMaybeThunk n >>= removeThunk
instance (Ix i, Hashable a) => Hashable (Array i a) where
hash arr = F.foldr combine 0 (fmap hash arr)
instance Hashable Value where
hash (VInt i) = combine 1 (hash i)
hash (VBool b) = combine 2 (hash b)
hash (VChar c) = combine 3 (hash c)
hash (VTuple vs) = combine 5 (hash vs)
hash (VDot vs) = combine 6 (hash vs)
hash (VChannel n) = combine 7 (hash n)
hash (VDataType n) = combine 8 (hash n)
hash (VList vs) = combine 9 (hash vs)
hash (VSet vset) = combine 10 (hash vset)
-- We identify all functions (for process names) - see comment below in Eq.
hash (VFunction id _) = combine 11 (hash id)
hash (VProc p) = combine 12 (hash p)
hash (VMap m) = combine 13 (hash (M.toList m))
instance Eq Value where
VInt i1 == VInt i2 = i1 == i2
VBool b1 == VBool b2 = b1 == b2
VChar c1 == VChar c2 = c1 == c2
VTuple vs1 == VTuple vs2 = vs1 == vs2
VDot vs1 == VDot vs2 = vs1 == vs2
VChannel n1 == VChannel n2 = n1 == n2
VDataType n1 == VDataType n2 = n1 == n2
VList vs1 == VList vs2 = vs1 == vs2
VSet s1 == VSet s2 = s1 == s2
VProc p1 == VProc p2 = p1 == p2
VFunction id1 _ == VFunction id2 _ = id1 == id2
VMap m1 == VMap m2 = m1 == m2
v1 == v2 = False
-- | Implements CSPM comparisons (note that Ord Value does not).
compareValues :: Value -> Value -> Maybe Ordering
-- The following are all orderable and comparable
compareValues (VInt i1) (VInt i2) = Just (compare i1 i2)
compareValues (VChar c1) (VChar c2) = Just (compare c1 c2)
compareValues (VBool b1) (VBool b2) = Just (compare b1 b2)
compareValues (VTuple vs1) (VTuple vs2) =
-- Tuples must be same length by type checking
-- Tuples are ordered lexiographically
let
(l, u) = bounds vs1
cmp ix | ix > u = EQ
cmp ix = compare (vs1!ix) (vs2!ix) `thenCmp` cmp (ix+1)
in Just (cmp 0)
compareValues (VList vs1) (VList vs2) =
let
-- for lists comparing means comparing prefixes
cmp [] [] = Just EQ
cmp [] (y:ys) = Just LT
cmp (x:xs) [] = Just GT
cmp (x:xs) (y:ys) | x == y = cmp xs ys
cmp (x:xs) (y:ys) =
-- x != y, hence neither can be a prefix of the other
Nothing
in cmp vs1 vs2
compareValues (VSet s1) (VSet s2) = S.compareValueSets s1 s2
compareValues (VMap m1) (VMap m2) =
let cmp v1 v2 =
case compareValues v1 v2 of
Just LT -> True
Just EQ -> True
_ -> False
in if m1 == m2 then Just EQ
else if M.isSubmapOfBy cmp m1 m2 then Just LT
else if M.isSubmapOfBy cmp m2 m1 then Just GT
else Nothing
-- The following can only be compared for equality, hence if they are not
-- equal we return Nothing.
compareValues (VChannel n1) (VChannel n2) =
if n1 == n2 then Just EQ else Nothing
compareValues (VDataType n1) (VDataType n2) =
if n1 == n2 then Just EQ else Nothing
compareValues (VDot vs1) (VDot vs2) =
if vs1 == vs2 then Just EQ else Nothing
-- Every other possibility is invalid
compareValues v1 v2 = panic $ "Cannot compare two values"
instance Ord Value where
-- This implementation is used for various internal measures, but not
-- for implementing actual comparisons in CSPM.
compare (VInt i1) (VInt i2) = compare i1 i2
compare (VChar c1) (VChar c2) = compare c1 c2
compare (VBool b1) (VBool b2) = compare b1 b2
compare (VTuple vs1) (VTuple vs2) = compare vs1 vs2
compare (VList vs1) (VList vs2) = compare vs1 vs2
compare (VSet s1) (VSet s2) = compare s1 s2
compare (VMap m1) (VMap m2) = compare m1 m2
-- These are only ever used for the internal set implementation
compare (VDot vs1) (VDot vs2) = compare vs1 vs2
compare (VChannel n) (VChannel n') = compare n n'
compare (VDataType n) (VDataType n') = compare n n'
compare (VProc p1) (VProc p2) = compare p1 p2
compare (VFunction id1 _) (VFunction id2 _) = compare id1 id2
compare v1 v2 = panic $
-- Must be as a result of a mixed set of values, which cannot happen
-- as a result of type checking.
"Internal sets - cannot order "
procName :: ScopeIdentifier -> ProcName
procName = ProcName
scopeId :: Name -> [[Value]] -> Maybe ScopeIdentifier -> ScopeIdentifier
scopeId n vss pn = SFunctionBind n (map (map trimValueForProcessName) vss) pn
annonymousScopeId :: [Value] -> Maybe ScopeIdentifier -> ScopeIdentifier
annonymousScopeId vss pn = SVariableBind (map trimValueForProcessName vss) pn
-- | This assumes that the value is a VDot with the left is a VChannel
valueEventToEvent :: Value -> Event
valueEventToEvent = UserEvent
errorThunk = panic "Trimmed value function evaluated"
trimFunctionIdentifier :: FunctionIdentifier -> FunctionIdentifier
trimFunctionIdentifier (FBuiltInFunction n args) =
FBuiltInFunction n (map trimValueForProcessName args)
trimFunctionIdentifier (FLambda e p) = FLambda e p
trimFunctionIdentifier (FMatchBind n args p) =
FMatchBind n (map (map trimValueForProcessName) args) p
trimValueForProcessName :: Value -> Value
trimValueForProcessName (VInt i) = VInt i
trimValueForProcessName (VChar c) = VChar c
trimValueForProcessName (VBool b) = VBool b
trimValueForProcessName (VTuple vs) = VTuple (fmap trimValueForProcessName vs)
trimValueForProcessName (VList vs) = VList (map trimValueForProcessName vs)
trimValueForProcessName (VSet s) =
VSet $ S.fromList $ map trimValueForProcessName $ S.toList s
trimValueForProcessName (VMap m) = VMap $ M.fromList $
map (\ (v1, v2) -> (trimValueForProcessName v1, trimValueForProcessName v2))
(M.toList m)
trimValueForProcessName (VDot vs) = VDot $ map trimValueForProcessName vs
trimValueForProcessName (VChannel n) = VChannel n
trimValueForProcessName (VDataType n) = VDataType n
trimValueForProcessName (VFunction id _) =
VFunction (trimFunctionIdentifier id) errorThunk
trimValueForProcessName (VProc p) = VProc (trimProcess p)