-
Notifications
You must be signed in to change notification settings - Fork 53
/
Copy pathValue.hs
248 lines (227 loc) · 8.25 KB
/
Value.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Values and environments used for interpreting the Swarm language.
module Swarm.Language.Value (
-- * Values
Value (..),
prettyValue,
valueToTerm,
-- * Environments
Env,
emptyEnv,
envTypes,
envReqs,
envVals,
envTydefs,
lookupValue,
addBinding,
addValueBinding,
addTydef,
) where
import Control.Lens hiding (Const)
import Data.Bool (bool)
import Data.Foldable (Foldable (..))
import Data.Hashable (Hashable)
import Data.Map (Map)
import Data.Map qualified as M
import Data.Set qualified as S
import Data.Set.Lens (setOf)
import Data.Text (Text)
import GHC.Generics (Generic)
import Swarm.Language.Context (Ctx)
import Swarm.Language.Context qualified as Ctx
import Swarm.Language.Key (KeyCombo, prettyKeyCombo)
import Swarm.Language.Requirements.Type (ReqCtx, Requirements)
import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction
import Swarm.Language.Typed
import Swarm.Language.Types (Polytype, TCtx, TDCtx, TydefInfo, Type)
import Swarm.Pretty (prettyText)
import Prelude hiding (Foldable (..))
-- | A /value/ is a term that cannot (or does not) take any more
-- evaluation steps on its own.
data Value where
-- | The unit value.
VUnit :: Value
-- | An integer.
VInt :: Integer -> Value
-- | Literal text.
VText :: Text -> Value
-- | A direction.
VDir :: Direction -> Value
-- | A boolean.
VBool :: Bool -> Value
-- | A reference to a robot.
VRobot :: Int -> Value
-- | An injection into a sum type. False = left, True = right.
VInj :: Bool -> Value -> Value
-- | A pair.
VPair :: Value -> Value -> Value
-- | A /closure/, representing a lambda term along with an
-- environment containing bindings for any free variables in the
-- body of the lambda.
VClo :: Var -> Term -> Env -> Value
-- | An application of a constant to some value arguments,
-- potentially waiting for more arguments. If a constant
-- application is fully saturated (as defined by its 'arity'),
-- whether it is a value or not depends on whether or not it
-- represents a command (as defined by 'isCmd'). If a command
-- (e.g. 'Build'), it is a value, and awaits an 'Swarm.Game.CESK.FExec' frame
-- which will cause it to execute. Otherwise (e.g. 'If'), it is
-- not a value, and will immediately reduce.
VCApp :: Const -> [Value] -> Value
-- | An unevaluated bind expression, waiting to be executed, of the
-- form /i.e./ @c1 ; c2@ or @x <- c1; c2@. We also store an 'Env'
-- in which to interpret the commands.
VBind :: Maybe Var -> Maybe Polytype -> Maybe Requirements -> Term -> Term -> Env -> Value
-- | A (non-recursive) delayed term, along with its environment. If
-- a term would otherwise be evaluated but we don't want it to be
-- (/e.g./ as in the case of arguments to an 'if', or a recursive
-- binding), we can stick a 'TDelay' on it, which turns it into a
-- value. Delayed terms won't be evaluated until 'Force' is
-- applied to them.
VDelay :: Term -> Env -> Value
-- | A reference to a memory cell in the store.
VRef :: Int -> Value
-- | An indirection to a value stored in a memory cell. The
-- difference between VRef and VIndir is that VRef is a "real"
-- value (of Ref type), whereas VIndir is just a placeholder. If
-- a VRef is encountered during evaluation, it is the final
-- result; if VIndir is encountered during evaluation, the value
-- it points to should be looked up.
VIndir :: Int -> Value
-- | A record value.
VRcd :: Map Var Value -> Value
-- | A keyboard input.
VKey :: KeyCombo -> Value
-- | A 'requirements' command awaiting execution.
VRequirements :: Text -> Term -> Env -> Value
-- | A 'suspend' command awaiting execution.
VSuspend :: Term -> Env -> Value
-- | A special value representing a program that terminated with
-- an exception.
VExc :: Value
-- | A special value used temporarily as the value for a variable
-- bound by a recursive let, while its definition is being
-- evaluated. If the variable is ever referenced again while its
-- value is still 'VBlackhole', that means it depends on itself in
-- a way that would trigger an infinite loop, and we can signal an
-- error. (Of course, we
-- <http://www.lel.ed.ac.uk/~gpullum/loopsnoop.html cannot detect
-- /all/ infinite loops this way>.)
VBlackhole :: Value
-- | A special value used to represent runtime type information
-- passed to ad-hoc polymorphic functions.
VType :: Type -> Value
deriving (Eq, Show, Generic, Hashable)
-- | A value context is a mapping from variable names to their runtime
-- values.
type VCtx = Ctx Value
--------------------------------------------------
-- Environments
--------------------------------------------------
-- | An environment is a record that stores relevant information for
-- all the variables currently in scope.
data Env = Env
{ _envTypes :: TCtx
-- ^ Map variables to their types.
, _envReqs :: ReqCtx
-- ^ Map variables to the capabilities required to evaluate/execute
-- them.
, _envVals :: VCtx
-- ^ Map variables to their values.
, _envTydefs :: TDCtx
-- ^ Type synonym definitions.
}
deriving (Eq, Show, Generic, Hashable)
makeLenses ''Env
emptyEnv :: Env
emptyEnv = Env Ctx.empty Ctx.empty Ctx.empty Ctx.empty
lookupValue :: Var -> Env -> Maybe Value
lookupValue x e = Ctx.lookup x (e ^. envVals)
addBinding :: Var -> Typed Value -> Env -> Env
addBinding x v = at x ?~ v
-- | Add a binding of a variable to a value *only* (no type and
-- requirements). NOTE that if we then try to look up the variable
-- name using the `At` instance for `Env`, it will report `Nothing`!
-- `lookupValue` will work though.
addValueBinding :: Var -> Value -> Env -> Env
addValueBinding x v = envVals %~ Ctx.addBinding x v
addTydef :: Var -> TydefInfo -> Env -> Env
addTydef x pty = envTydefs %~ Ctx.addBinding x pty
instance Semigroup Env where
Env t1 r1 v1 td1 <> Env t2 r2 v2 td2 = Env (t1 <> t2) (r1 <> r2) (v1 <> v2) (td1 <> td2)
instance Monoid Env where
mempty = Env mempty mempty mempty mempty
instance AsEmpty Env
type instance Index Env = Ctx.Var
type instance IxValue Env = Typed Value
instance Ixed Env
instance At Env where
at name = lens getter setter
where
getter ctx =
do
typ <- Ctx.lookup name (ctx ^. envTypes)
val <- Ctx.lookup name (ctx ^. envVals)
req <- Ctx.lookup name (ctx ^. envReqs)
return $ Typed val typ req
setter ctx Nothing =
ctx
& envTypes
%~ Ctx.delete name
& envVals
%~ Ctx.delete name
& envReqs
%~ Ctx.delete name
setter ctx (Just (Typed val typ req)) =
ctx
& envTypes
%~ Ctx.addBinding name typ
& envVals
%~ Ctx.addBinding name val
& envReqs
%~ Ctx.addBinding name req
------------------------------------------------------------
-- Pretty-printing for values
------------------------------------------------------------
-- | Pretty-print a value.
prettyValue :: Value -> Text
prettyValue = prettyText . valueToTerm
-- | Inject a value back into a term.
valueToTerm :: Value -> Term
valueToTerm = \case
VUnit -> TUnit
VInt n -> TInt n
VText s -> TText s
VDir d -> TDir d
VBool b -> TBool b
VRobot r -> TRobot r
VInj s v -> TApp (TConst (bool Inl Inr s)) (valueToTerm v)
VPair v1 v2 -> TPair (valueToTerm v1) (valueToTerm v2)
VClo x t e ->
M.foldrWithKey
( \y v -> case v of
VIndir {} -> id
_ -> TLet LSLet False y Nothing Nothing Nothing (valueToTerm v)
)
(TLam x Nothing t)
(M.restrictKeys (Ctx.unCtx (e ^. envVals)) (S.delete x (setOf freeVarsV (Syntax' NoLoc t Empty ()))))
VCApp c vs -> foldl' TApp (TConst c) (reverse (map valueToTerm vs))
VBind mx mty mreq c1 c2 _ -> TBind mx mty mreq c1 c2
VDelay t _ -> TDelay t
VRef n -> TRef n
VIndir n -> TRef n
VRcd m -> TRcd (Just . valueToTerm <$> m)
VKey kc -> TApp (TConst Key) (TText (prettyKeyCombo kc))
VRequirements x t _ -> TRequirements x t
VSuspend t _ -> TSuspend t
VExc -> TConst Undefined
VBlackhole -> TConst Undefined
VType _ -> TConst Undefined