Skip to content
Navigation Menu
{{ message }}
This repository was archived by the owner on Apr 1, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 459
Expand file tree
/
Copy pathValue.hs
More file actions
479 lines (391 loc) · 17.6 KB
/
Copy pathValue.hs
File metadata and controls
479 lines (391 loc) · 17.6 KB
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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module Control.Abstract.Value
( AbstractValue(..)
, AbstractIntro(..)
, Comparator(..)
-- * Domain effects
-- $domainEffects
, function
, BuiltIn(..)
, bindThis
, builtIn
, call
, Function(..)
, runFunction
, FunctionC(..)
, boolean
, asBool
, ifthenelse
, Boolean(..)
, runBoolean
, BooleanC(..)
, while
, doWhile
, forLoop
, While(..)
, runWhile
, WhileC(..)
, unit
, Unit(..)
, runUnit
, UnitC(..)
, string
, asString
, String(..)
, StringC(..)
, runString
, integer
, float
, rational
, liftNumeric
, liftNumeric2
, Numeric(..)
, NumericC(..)
, object
, scopedEnvironment
, klass
, Object(..)
, ObjectC(..)
, runObject
, runNumeric
, runNumericFunction
, runNumeric2Function
, castToInteger
, runBitwiseFunction
, runBitwise2Function
, liftBitwise
, liftBitwise2
, unsignedRShift
, Bitwise(..)
, BitwiseC(..)
, runBitwise
, array
, asArray
, Array(..)
, ArrayC(..)
, runArray
, hash
, kvPair
, Hash(..)
, runHash
, HashC(..)
) where
import Analysis.Name
import Control.Abstract.Evaluator
import Control.Abstract.Heap
import Control.Abstract.ScopeGraph (CurrentScope, Declaration, ScopeGraph)
import Control.Algebra
import Control.Carrier.Reader
import Data.Abstract.BaseError
import Data.Abstract.Module
import Data.Abstract.Number (Number, SomeNumber)
import Data.Bits
import Data.Scientific (Scientific)
import Data.Text (Text)
import GHC.Generics (Generic, Generic1)
import Prelude hiding (String)
import Source.Span
-- | This datum is passed into liftComparison to handle the fact that Ruby and PHP
-- have built-in generalized-comparison ("spaceship") operators. If you want to
-- encapsulate a traditional, boolean-returning operator, wrap it in 'Concrete';
-- if you want the generalized comparator, pass in 'Generalized'. In 'AbstractValue'
-- instances, you can then then handle the different cases to return different
-- types, if that's what you need.
data Comparator
= Concrete (forall a . Ord a => a -> a -> Bool)
| Generalized
-- Domain effects
-- $domainEffects
-- Value effects are effects modelling the /introduction/ & /elimination/ of some specific kind of value.
--
-- Modelling each of these as effects has several advantages∷
--
-- * It is strictly more flexible than modelling them as methods in a typeclass, as effect list–indexed typeclasses must be constrained at every list of effects at which they can be applied, whereas effect membership constraints can be deduced recursively (i.e. if @X@ is constrained to be a member of @effects@, it is automatically deducible that it is also a member of @Y \': effects@).
-- * It offers us the potential of specializing the handlers on a language-by-language basis without the need for extra type parameters (albeit at the cost of automatic selection).
-- * It offers us the potential of fine-grained specialization of the abstract domain, enabling convenient, piecemeal customization of the domain, and even semi-abstract domains.
-- * Finally, it should eventually allow us to customize _which_ value effects are available for a given language; e.g. a language without OO features would not require OO value effects. (This would also rely on 'Evaluatable' instances being able to specify membership constraints, which is not currently possible.)
--
-- In the concrete domain, introductions & eliminations respectively construct & pattern match against values, while in abstract domains they respectively construct & project finite sets of discrete observations of abstract values. For example, an abstract domain modelling integers as a sign (-, 0, or +) would introduce abstract values by mapping integers to their sign and eliminate them by mapping signs back to some canonical integer, e.g. - -> -1, 0 -> 0, + -> 1.
function :: Has (Function term address value) sig m => Name -> [Name] -> term -> address -> Evaluator term address value m value
function name params body scope = sendFunction (Function name params body scope pure)
data BuiltIn
= Print
| Show
deriving (Eq, Ord, Show, Generic)
builtIn :: Has (Function term address value) sig m => address -> BuiltIn -> Evaluator term address value m value
builtIn address = sendFunction . flip (BuiltIn address) pure
call :: Has (Function term address value) sig m => value -> [value] -> Evaluator term address value m value
call fn args = sendFunction (Call fn args pure)
sendFunction :: Has (Function term address value) sig m => Function term address value (Evaluator term address value m) a -> Evaluator term address value m a
sendFunction = send
bindThis :: Has (Function term address value) sig m => value -> value -> Evaluator term address value m value
bindThis this that = sendFunction (Bind this that pure)
data Function term address value (m :: * -> *) k
= Function Name [Name] term address (value -> m k) -- ^ A function is parameterized by its name, parameter names, body, parent scope, and returns a ValueRef.
| BuiltIn address BuiltIn (value -> m k) -- ^ A built-in is parameterized by its parent scope, BuiltIn type, and returns a value.
| Call value [value] (value -> m k) -- ^ A Call takes a set of values as parameters and returns a ValueRef.
| Bind value value (value -> m k)
deriving (Functor, Generic1)
instance HFunctor (Function term address value)
instance Effect (Function term address value)
runFunction :: (term -> Evaluator term address value (FunctionC term address value m) value)
-> Evaluator term address value (FunctionC term address value m) a
-> Evaluator term address value m a
runFunction eval = raiseHandler (runReader (runEvaluator . eval) . runFunctionC)
newtype FunctionC term address value m a = FunctionC { runFunctionC :: ReaderC (term -> FunctionC term address value m value) m a }
deriving (Alternative, Applicative, Functor, Monad)
-- | Construct a boolean value in the abstract domain.
boolean :: Has (Boolean value) sig m => Bool -> m value
boolean = send . flip Boolean pure
-- | Extract a 'Bool' from a given value.
asBool :: Has (Boolean value) sig m => value -> m Bool
asBool = send . flip AsBool pure
-- | Eliminate boolean values. TODO: s/boolean/truthy
ifthenelse :: Has (Boolean value) sig m => value -> m a -> m a -> m a
ifthenelse v t e = asBool v >>= \ c -> if c then t else e
data Boolean value (m :: * -> *) k
= Boolean Bool (value -> m k)
| AsBool value (Bool -> m k)
deriving (Functor, Generic1)
instance HFunctor (Boolean value)
instance Effect (Boolean value)
runBoolean :: Evaluator term address value (BooleanC value m) a
-> Evaluator term address value m a
runBoolean = raiseHandler runBooleanC
newtype BooleanC value m a = BooleanC { runBooleanC :: m a }
deriving (Alternative, Applicative, Functor, Monad)
-- | The fundamental looping primitive, built on top of 'ifthenelse'.
while :: Has (While value) sig m
=> Evaluator term address value m value -- ^ Condition
-> Evaluator term address value m value -- ^ Body
-> Evaluator term address value m value
while cond body = send (While cond body pure)
-- | Do-while loop, built on top of while.
doWhile :: Has (While value) sig m
=> Evaluator term address value m value -- ^ Body
-> Evaluator term address value m value -- ^ Condition
-> Evaluator term address value m value
doWhile body cond = body *> while cond body
-- | C-style for loops.
forLoop :: ( Has (Allocator address) sig m
, Has (Reader ModuleInfo) sig m
, Has (Reader Span) sig m
, Has (Resumable (BaseError (HeapError address))) sig m
, Has (State (Heap address address value)) sig m
, Has (State (ScopeGraph address)) sig m
, Has (Reader (CurrentFrame address)) sig m
, Has (Reader (CurrentScope address)) sig m
, Has (While value) sig m
, Has Fresh sig m
, Ord address
)
=> Evaluator term address value m value -- ^ Initial statement
-> Evaluator term address value m value -- ^ Condition
-> Evaluator term address value m value -- ^ Increment/stepper
-> Evaluator term address value m value -- ^ Body
-> Evaluator term address value m value
forLoop initial cond step body = initial *> while cond (withLexicalScopeAndFrame body *> step)
data While value m k
= While (m value) (m value) (value -> m k)
deriving (Functor, Generic1)
instance HFunctor (While value) where
hmap f (While cond body k) = While (f cond) (f body) (f . k)
runWhile :: Evaluator term address value (WhileC value m) a
-> Evaluator term address value m a
runWhile = raiseHandler runWhileC
newtype WhileC value m a = WhileC { runWhileC :: m a }
deriving (Alternative, Applicative, Functor, Monad)
-- | Construct an abstract unit value.
unit :: Has (Unit value) sig m => Evaluator term address value m value
unit = send (Unit pure)
newtype Unit value (m :: * -> *) k
= Unit (value -> m k)
deriving (Functor, Generic1)
instance HFunctor (Unit value)
instance Effect (Unit value)
runUnit :: Evaluator term address value (UnitC value m) a
-> Evaluator term address value m a
runUnit = raiseHandler runUnitC
newtype UnitC value m a = UnitC { runUnitC :: m a }
deriving (Alternative, Applicative, Functor, Monad)
-- | Construct a String value in the abstract domain.
string :: Has (String value) sig m => Text -> m value
string t = send (String t pure)
-- | Extract 'Text' from a given value.
asString :: Has (String value) sig m => value -> m Text
asString v = send (AsString v pure)
data String value (m :: * -> *) k
= String Text (value -> m k)
| AsString value (Text -> m k)
deriving (Functor, Generic1)
instance HFunctor (String value)
instance Effect (String value)
newtype StringC value m a = StringC { runStringC :: m a }
deriving (Alternative, Applicative, Functor, Monad)
runString :: Evaluator term address value (StringC value m) a
-> Evaluator term address value m a
runString = raiseHandler runStringC
-- | Construct an abstract integral value.
integer :: Has (Numeric value) sig m => Integer -> m value
integer t = send (Integer t pure)
-- | Construct a floating-point value.
float :: Has (Numeric value) sig m => Scientific -> m value
float t = send (Float t pure)
-- | Construct a rational value.
rational :: Has (Numeric value) sig m => Rational -> m value
rational t = send (Rational t pure)
-- | Lift a unary operator over a 'Num' to a function on 'value's.
liftNumeric :: Has (Numeric value) sig m
=> (forall a . Num a => a -> a)
-> value
-> m value
liftNumeric t v = send (LiftNumeric (NumericFunction t) v pure)
-- | Lift a pair of binary operators to a function on 'value's.
-- You usually pass the same operator as both arguments, except in the cases where
-- Haskell provides different functions for integral and fractional operations, such
-- as division, exponentiation, and modulus.
liftNumeric2 :: Has (Numeric value) sig m
=> (forall a b. Number a -> Number b -> SomeNumber)
-> value
-> value
-> m value
liftNumeric2 t v1 v2 = send (LiftNumeric2 (Numeric2Function t) v1 v2 pure)
newtype NumericFunction = NumericFunction { runNumericFunction :: forall a . Num a => a -> a }
newtype Numeric2Function = Numeric2Function { runNumeric2Function :: forall a b. Number a -> Number b -> SomeNumber }
data Numeric value (m :: * -> *) k
= Integer Integer (value -> m k)
| Float Scientific (value -> m k)
| Rational Rational (value -> m k)
| LiftNumeric NumericFunction value (value -> m k)
| LiftNumeric2 Numeric2Function value value (value -> m k)
deriving (Functor, Generic1)
instance HFunctor (Numeric value)
instance Effect (Numeric value)
newtype NumericC value m a = NumericC { runNumericC :: m a }
deriving (Alternative, Applicative, Functor, Monad)
runNumeric :: Evaluator term address value (NumericC value m) a
-> Evaluator term address value m a
runNumeric = raiseHandler runNumericC
-- | Cast numbers to integers
castToInteger :: Has (Bitwise value) sig m => value -> m value
castToInteger t = send (CastToInteger t pure)
-- | Lift a unary bitwise operator to values. This is usually 'complement'.
liftBitwise :: Has (Bitwise value) sig m
=> (forall a . Bits a => a -> a)
-> value
-> m value
liftBitwise t v = send (LiftBitwise (BitwiseFunction t) v pure)
-- | Lift a binary bitwise operator to values. The Integral constraint is
-- necessary to satisfy implementation details of Haskell left/right shift,
-- but it's fine, since these are only ever operating on integral values.
liftBitwise2 :: Has (Bitwise value) sig m
=> (forall a . (Integral a, Bits a) => a -> a -> a)
-> value
-> value
-> m value
liftBitwise2 t v1 v2 = send (LiftBitwise2 (Bitwise2Function t) v1 v2 pure)
unsignedRShift :: Has (Bitwise value) sig m
=> value
-> value
-> m value
unsignedRShift v1 v2 = send (UnsignedRShift v1 v2 pure)
newtype BitwiseFunction = BitwiseFunction { runBitwiseFunction :: forall a . Bits a => a -> a }
newtype Bitwise2Function = Bitwise2Function { runBitwise2Function :: forall a . (Integral a, Bits a) => a -> a -> a }
data Bitwise value (m :: * -> *) k
= CastToInteger value (value -> m k)
| LiftBitwise BitwiseFunction value (value -> m k)
| LiftBitwise2 Bitwise2Function value value (value -> m k)
| UnsignedRShift value value (value -> m k)
deriving (Functor, Generic1)
instance HFunctor (Bitwise value)
instance Effect (Bitwise value)
runBitwise :: Evaluator term address value (BitwiseC value m) a
-> Evaluator term address value m a
runBitwise = raiseHandler runBitwiseC
newtype BitwiseC value m a = BitwiseC { runBitwiseC :: m a }
deriving (Alternative, Applicative, Functor, Monad)
object :: Has (Object address value) sig m => address -> m value
object address = send (Object address pure)
-- | Extract the environment from any scoped object (e.g. classes, namespaces, etc).
scopedEnvironment :: Has (Object address value) sig m => value -> m (Maybe address)
scopedEnvironment value = send (ScopedEnvironment value pure)
-- | Build a class value from a name and environment.
-- declaration is the new class's identifier
-- address is the environment to capture
klass :: Has (Object address value) sig m => Declaration -> address -> m value
klass d a = send (Klass d a pure)
data Object address value m k
= Object address (value -> m k)
| ScopedEnvironment value (Maybe address -> m k)
| Klass Declaration address (value -> m k)
deriving (Functor, Generic1)
instance HFunctor (Object address value)
instance Effect (Object address value)
newtype ObjectC address value m a = ObjectC { runObjectC :: m a }
deriving (Alternative, Applicative, Functor, Monad)
runObject :: Evaluator term address value (ObjectC address value m) a
-> Evaluator term address value m a
runObject = raiseHandler runObjectC
-- | Construct an array of zero or more values.
array :: Has (Array value) sig m => [value] -> m value
array v = send (Array v pure)
asArray :: Has (Array value) sig m => value -> m [value]
asArray v = send (AsArray v pure)
data Array value (m :: * -> *) k
= Array [value] (value -> m k)
| AsArray value ([value] -> m k)
deriving (Functor, Generic1)
instance HFunctor (Array value)
instance Effect (Array value)
newtype ArrayC value m a = ArrayC { runArrayC :: m a }
deriving (Alternative, Applicative, Functor, Monad)
runArray :: Evaluator term address value (ArrayC value m) a
-> Evaluator term address value m a
runArray = raiseHandler runArrayC
-- | Construct a hash out of pairs.
hash :: Has (Hash value) sig m => [(value, value)] -> m value
hash v = send (Hash v pure)
-- | Construct a key-value pair for use in a hash.
kvPair :: Has (Hash value) sig m => value -> value -> m value
kvPair v1 v2 = send (KvPair v1 v2 pure)
data Hash value (m :: * -> *) k
= Hash [(value, value)] (value -> m k)
| KvPair value value (value -> m k)
deriving (Functor, Generic1)
instance HFunctor (Hash value)
instance Effect (Hash value)
newtype HashC value m a = HashC { runHashC :: m a }
deriving (Alternative, Applicative, Functor, Monad)
runHash :: Evaluator term address value (HashC value m) a
-> Evaluator term address value m a
runHash = raiseHandler runHashC
class Show value => AbstractIntro value where
-- | Construct the nil/null datatype.
null :: value
-- | A 'Monad' abstracting the evaluation of (and under) binding constructs (functions, methods, etc).
--
-- This allows us to abstract the choice of whether to evaluate under binders for different value types.
class AbstractIntro value => AbstractValue term address value carrier where
-- | Lift a Comparator (usually wrapping a function like == or <=) to a function on values.
liftComparison :: Comparator -> (value -> value -> Evaluator term address value carrier value)
-- | Construct an N-ary tuple of multiple (possibly-disjoint) values
tuple :: [value] -> Evaluator term address value carrier value
-- | Extract the contents of a key-value pair as a tuple.
asPair :: value -> Evaluator term address value carrier (value, value)
-- | @index x i@ computes @x[i]@, with zero-indexing.
index :: value -> value -> Evaluator term address value carrier value
-- | Build a namespace value from a name and environment stack
--
-- Namespaces model closures with monoidal environments.
namespace :: Name -- ^ The namespace's identifier
-> address -- ^ The frame of the namespace.
-> Evaluator term address value carrier value
You can’t perform that action at this time.
