@@ -5,31 +5,38 @@ module Contract.Expr
5
5
, ExprG -- no constructors exported!
6
6
, BoolE , IntE , RealE
7
7
, i , r , b , v , pair , first , second , acc , obs , chosenBy
8
- , ppExp , certainExp , eqExp , translExp
8
+ , certainExp , eqExp , translExp
9
9
-- evaluation. Polymorphic eval not exported
10
10
, Env , emptyEnv
11
11
, evalI , evalR , evalB , simplifyExp
12
12
) where
13
13
14
-
15
14
-- to define the exception
16
15
import Control.Exception
17
16
import Data.Typeable
17
+
18
+ -- for name supply
18
19
import System.IO.Unsafe (unsafePerformIO )
19
20
import Control.Concurrent
20
21
22
+ -- for pretty-printer
23
+ import Text.Printf
24
+
25
+
21
26
data Currency = EUR | DKK | SEK | USD | GBP | JPY
27
+ deriving (Eq , Show , Read )
22
28
-- good enough with only FX derivatives. Otherwise we could add this:
23
29
-- "... | Stock String | Equity String"
24
30
-- These are just tags, not used in expressions / arithmetics
25
31
-- (otherwise we might want a GADT for them)
26
32
27
- ppCur EUR = " EUR"
28
- ppCur DKK = " DKK"
29
- ppCur SEK = " SEK"
30
- ppCur USD = " USD"
31
- ppCur GBP = " GBP"
32
- ppCur JPY = " JPY"
33
+ -- ppCur not needed, use "show"
34
+ -- ppCur EUR = "EUR"
35
+ -- ppCur DKK = "DKK"
36
+ -- ppCur SEK = "SEK"
37
+ -- ppCur USD = "USD"
38
+ -- ppCur GBP = "GBP"
39
+ -- ppCur JPY = "JPY"
33
40
34
41
-- submodule expression starts here
35
42
type Var = String
@@ -58,6 +65,29 @@ data ExprG a where
58
65
Equal :: Eq a => ExprG a -> ExprG a -> ExprG Bool
59
66
Or :: ExprG Bool -> ExprG Bool -> ExprG Bool
60
67
68
+ -- | Show instance for debugging (cannot be derived automatically for GADTs)
69
+ instance Show (ExprG a ) where
70
+ show (I n) = " I " ++ show n
71
+ show (R r) = " R " ++ ppReal r
72
+ show (B b) = " B " ++ show b
73
+ show (V v) = " V " ++ show v
74
+ show (Pair e1 e2) = " Pair " ++ par (show e1) ++ par (show e2)
75
+ show (Fst e) = " Fst " ++ par (show e)
76
+ show (Snd e) = " Snd " ++ par (show e)
77
+ show (Obs (s,i)) = " Obs " ++ par (show s ++ " ," ++ show i)
78
+ show (ChosenBy (p,i)) = " ChosenBy " ++ par (show p ++ " ," ++ show i)
79
+ show (Acc (v,e) i a) = " Acc " ++ unwords [par (show v ++ " ," ++ show e),
80
+ show i, par (show a)]
81
+ show (Not e) = " Not " ++ par (show e)
82
+ show (Arith op e1 e2) = " Arith " ++ unwords [show op, par (show e1),
83
+ par (show e2)]
84
+ show (Less e1 e2) = " Less " ++ unwords [par (show e1), par (show e2)]
85
+ show (Equal e1 e2) = " Equal " ++ unwords [par (show e1), par (show e2)]
86
+ show (Or e1 e2) = " Or " ++ unwords [par (show e1), par (show e2)]
87
+
88
+ -- parenthesis around a string
89
+ par s = " (" ++ s ++ " )"
90
+
61
91
data AOp = Plus | Minus | Times | Max | Min
62
92
deriving (Show )
63
93
@@ -66,8 +96,8 @@ ppOp :: AOp -> (String,Bool)
66
96
ppOp Plus = (" +" , True )
67
97
ppOp Minus = (" -" , True )
68
98
ppOp Times = (" *" , True )
69
- ppOp Max = (" max" , False )
70
- ppOp Min = (" min" , False )
99
+ ppOp Max = (" max " , False )
100
+ ppOp Min = (" min " , False )
71
101
72
102
-- reading operators
73
103
instance Read AOp where
@@ -100,24 +130,26 @@ opsem Times = (*)
100
130
opsem Max = max
101
131
opsem Min = min
102
132
103
- -- Num instance, enabling us to write "e1 + e2" for ExprG a with Num a
133
+ -- | Num instance, enabling us to write "e1 + e2" for ExprG a with Num a
104
134
instance (Decompose (ExprG a ), Num (Content (ExprG a )), Num a ) =>
105
135
Num (ExprG a ) where
106
- (+) = Arith Plus
107
- (*) = Arith Times
108
- (-) = Arith Minus
109
- negate = Arith Minus (fromInteger 0 )
136
+ (+) = arith Plus
137
+ (*) = arith Times
138
+ (-) = arith Minus
139
+ negate = arith Minus (fromInteger 0 )
110
140
abs a = (constr a) (abs (content a))
111
141
signum a = (constr a) (signum (content a))
112
142
fromInteger n = (constr (undefined :: ExprG a )) (fromInteger n)
113
143
-- there's a pattern... f a = (constr a) (f (content a))
114
144
115
- -- enabled with this - slightly weird - helper class
145
+ -- | Num instances are possible through this - slightly weird - helper
146
+ -- class which extracts constructors and values
116
147
class Num a => Decompose a where
117
148
type Content a
118
149
constr :: a -> (Content a -> a )
119
150
content :: Num (Content a ) => a -> Content a
120
151
152
+ -- NB do we _ever_ use Int expressions? Maybe dump this whole weird thing
121
153
instance Decompose (ExprG Int ) where
122
154
type Content (ExprG Int ) = Int
123
155
constr _ = I
@@ -128,6 +160,8 @@ instance Decompose (ExprG Double) where
128
160
constr _ = R
129
161
content x = evalR emptyEnv x
130
162
163
+ -- the smart constructors of the interface (here: simple)
164
+
131
165
i = I -- :: Int -> IntE
132
166
r = R -- :: Double -> RealE
133
167
b = B -- :: Bool -> BoolE
@@ -137,12 +171,23 @@ first = Fst
137
171
second = Snd
138
172
obs = Obs
139
173
chosenBy = ChosenBy
174
+ (!<!) :: Ord a => ExprG a -> ExprG a -> ExprG Bool
175
+ (!<!) = Less
176
+ (!=!) :: Eq a => ExprG a -> ExprG a -> ExprG Bool
177
+ (!=!) = Equal
178
+
179
+ infixl 4 !<!
180
+ infixl 4 !=!
181
+
182
+ -- +, -, * come from the Num instance
183
+ maxx ,minn :: Num a => ExprG a -> ExprG a -> ExprG a
184
+ maxx = Arith Max -- instance magic would require an Ord instance...
185
+ minn = Arith Min -- ...which requires an Eq instance
140
186
141
187
acc :: (Num a ) => (ExprG a -> ExprG a ) -> Int -> ExprG a -> ExprG a
142
188
acc _ 0 a = a
143
189
acc f i a = let v = newName " v"
144
190
in Acc (v,f (V v)) i a
145
-
146
191
-- using a unique supply, the quick way...
147
192
{-# NOINLINE idSupply #-}
148
193
idSupply :: MVar Int
@@ -152,16 +197,16 @@ newName s = unsafePerformIO (do next <- takeMVar idSupply
152
197
putMVar idSupply (next+ 1 )
153
198
return (s ++ show next))
154
199
155
- -- equality: comparing syntax by hash, considering commutativity
200
+
201
+ -- | expression equality, comparing their syntax by hash
156
202
eqExp :: ExprG a -> ExprG a -> Bool
157
203
eqExp e1 e2 = hashExp e1 == hashExp e2
158
204
205
+ -- | Compute a hash of an expression, for syntactic comparisons. Considers commutativity by symmetric hashing scheme for commutative operations.
159
206
hashExp :: ExprG a -> Integer
160
- hashExp e = error " not defined yet"
161
-
162
- ppExp :: ExprG a -> String
163
- ppExp e = error " not defined yet"
207
+ hashExp e = error " must be copied from SML code"
164
208
209
+ -- | Does an expression contain any observables or choices?
165
210
certainExp :: ExprG a -> Bool
166
211
certainExp e = case e of
167
212
V _ -> False -- if variables are used only for functions in Acc, we could return true here!
@@ -180,6 +225,7 @@ certainExp e = case e of
180
225
Equal e1 e2 -> certainExp e1 && certainExp e2
181
226
Or e1 e2 -> certainExp e1 && certainExp e2
182
227
228
+ -- | translating an expression in time
183
229
translExp :: ExprG a -> Int -> ExprG a
184
230
translExp e 0 = e
185
231
translExp e d =
@@ -200,8 +246,55 @@ translExp e d =
200
246
Equal e1 e2 -> Equal (translExp e1 d) (translExp e2 d)
201
247
Or e1 e2 -> Or (translExp e1 d) (translExp e2 d)
202
248
249
+ -----------------------------------------------------------------
250
+ -- Pretty-print an expression (not the same as the Show instance)
251
+
252
+ -- | internal: convert daycount to years/months/days, using 30/360 convention
253
+ ppTime :: Int -> String
254
+ ppTime 0 = " 0d"
255
+ ppTime t = if null s then " 0d" else s
256
+ where years = t `div` 360
257
+ months = (t `div` 30 ) `mod` 12 -- (t mod 360) div 30
258
+ days = t `mod` 30
259
+ str n c = if n == 0 then " " else show n ++ c: []
260
+ s = concat (zipWith str [years,months,days] " ymd" )
261
+
262
+ -- | real numbers printed with four decimal places (FX fashion)
263
+ ppReal :: Double -> String
264
+ ppReal = printf " %.4f"
265
+
266
+ -- | internal: print an expression, using an int printing function
267
+ ppExp0 :: (Int -> String ) -> ExprG a -> String
268
+ ppExp0 ppTime e =
269
+ case e of
270
+ V s -> s
271
+ I i -> ppTime i
272
+ R r -> ppReal r
273
+ B b -> show b
274
+ Pair e1 e2 -> par (ppExp0 ppTime e1 ++ " ," ++ ppExp0 ppTime e2)
275
+ Fst e -> " first" ++ par (ppExp0 ppTime e)
276
+ Snd e -> " second" ++ par (ppExp0 ppTime e)
277
+ Acc f i e -> " acc" ++ par(ppFun f ++ " ," ++ show i ++ " ," ++ ppExp e)
278
+ Obs (s,off) -> " Obs" ++ par (s ++ " @" ++ ppTime off)
279
+ ChosenBy (p,i) -> " Chosen by " ++ p ++ " @ " ++ ppTime i
280
+ Not e1 -> " not" ++ par (ppExp e1)
281
+ Arith op e1 e2 -> let (c,infx) = ppOp op
282
+ in if infx then par(ppExp e1 ++ c ++ ppExp e2)
283
+ else c ++ par (ppExp e1) ++ ' ' : par(ppExp e2)
284
+ Less e1 e2 -> par(ppExp0 ppTime e1 ++ " < " ++ ppExp0 ppTime e2)
285
+ Equal e1 e2 -> par(ppExp0 ppTime e1 ++ " ==" ++ ppExp0 ppTime e2)
286
+ Or e1 e2 -> par(ppExp e1 ++ " ||" ++ ppExp e2)
287
+ where ppExp e = ppExp0 ppTime e
288
+ ppFun (v,e) = " \\ " ++ v ++ " -> " ++ ppExp e
289
+
290
+ -- | pretty-printing an expression, using normal printer for Int
291
+ ppExp = ppExp0 show
292
+ -- | pretty-printing an expression, using time printer for Int
293
+ ppTimeExp = ppExp0 ppTime
294
+
203
295
--------------------------------------------------------------
204
- -- Evaluation:
296
+ -- Evaluation of expressions:
297
+
205
298
data EvalExc = Eval String deriving (Read ,Show ,Typeable )
206
299
instance Exception EvalExc
207
300
@@ -264,5 +357,6 @@ eval env e =
264
357
(_, B True ) -> B True
265
358
(bb1, bb2) -> Or bb1 bb2
266
359
360
+ -- | simplify an expression, using an environment
267
361
simplifyExp env e = eval env e
268
362
0 commit comments