-
Notifications
You must be signed in to change notification settings - Fork 0
/
test7.hs
466 lines (397 loc) · 13.9 KB
/
test7.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
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
import Data.Maybe
-- All programs are assumed to be well-formed in the following sense:
--
-- All operators, functions and procedures will always be applied
-- to the correct number of arguments, all of which will be of the appropriate
-- type.
--
-- Boolean-valued expressions will always evaluate to either 0 (false) or 1
-- (true).
--
-- In an array element assignment the array being assigned to will always be
-- in scope.
--
-- In a procedure call of the form Call x p es the procedure p will always exit
-- via a Return statement.
--
-- A Return statement will always be the last statement to be executed in a
-- procedure's defining code block (there is no `dead code').
--
--------------------------------------------------------------------
type Id = String
data Value = I Int | A [(Int, Int)]
deriving (Eq, Show)
data Op = Add | Mul | Less | Equal | Index
deriving (Eq, Show)
data Exp = Const Value |
Var Id |
OpApp Op Exp Exp |
Cond Exp Exp Exp |
FunApp Id [Exp]
deriving (Eq, Show)
type FunDef = (Id, ([Id], Exp))
type Block = [Statement]
data Statement = Assign Id Exp |
AssignA Id Exp Exp |
If Exp Block Block |
While Exp Block |
Call Id Id [Exp] |
Return Exp
deriving (Eq, Show)
type ProcDef = (Id, ([Id], Block))
data Scope = Local | Global
deriving (Eq, Show)
type Binding = (Id, (Scope, Value))
type State = [Binding]
--------------------------------------------------------------------
-- Part I
getValue :: Id -> State -> Value
-- Pre: The identifier has a binding in the state
getValue id state
= val
where
(_, val) = lookUp id state
getLocals :: State -> State
getLocals
= filter (\(_, (scope, _)) -> scope == Local)
getGlobals :: State -> State
getGlobals
= filter (\(_, (scope, _)) -> scope == Global)
assignArray :: Value -> Value -> Value -> Value
-- The arguments are the array, index and (new) value respectively
-- Pre: The three values have the appropriate value types (array (A),
-- integer (I) and integer (I)) respectively.
assignArray (A arr) (I i) (I v)
= A ((i,v) : filteredArr)
where
filteredArr = filter (\(x,y) -> i /= x) arr
updateVar :: (Id, Value) -> State -> State
updateVar (id, val) xs
| bindings == 0 = (id, (Local, val)) : xs
| bindings >= 2 = locVal : mappedList
| otherwise = mappedList
where
mappedList = map (\o@(i, (s, _)) -> if id == i then (i, (s, val)) else o) xs
bindings = length (filter (\(i,(_,_)) -> i == id) xs)
locVal = (id, (Local, val))
---------------------------------------------------------------------
-- Part II
applyOp :: Op -> Value -> Value -> Value
-- Pre: The values have the appropriate types (I or A) for each primitive
applyOp Add (I x) (I y)
= I (x+y)
applyOp Mul (I x) (I y)
= I (x*y)
applyOp Less (I x) (I y)
= I (boolToNum (x<y))
applyOp Equal (I x) (I y)
= I (boolToNum (x==y))
applyOp Index (A ((index, val):xs)) (I index')
| index == index' = (I val)
| otherwise = applyOp Index (A xs) (I index')
applyOp Index (A []) _
= I 0
boolToNum b = if b then 1 else 0
bindArgs :: [Id] -> [Value] -> State
-- Pre: the lists have the same length
bindArgs
= zipWith (\id val -> (id, (Local, val)))
evalArgs :: [Exp] -> [FunDef] -> State -> [Value]
evalArgs es fs state
= map (\e -> eval e fs state) es
eval :: Exp -> [FunDef] -> State -> Value
-- Pre: All expressions are well formed
-- Pre: All variables referenced have bindings in the given state
eval (Const c) _ _
= c
eval (Var id) _ state
= getValue id state
eval (Cond predicate e e') fs state
| bool == 1 = eval e fs state
| otherwise = eval e' fs state
where
(I bool) = eval predicate fs state
eval (OpApp op e e') fs state
= applyOp op newE newE'
where
newE = eval e fs state
newE'= eval e' fs state
eval (FunApp f es) fs state
= eval e fs (bindedA ++ state)
where
(as, e) = lookUp f fs
vs = evalArgs es fs state
bindedA = bindArgs as vs
---------------------------------------------------------------------
-- Part III
executeStatement :: Statement -> [FunDef] -> [ProcDef] -> State -> State
-- Pre: All statements are well formed
-- Pre: For array element assignment (AssignA) the array variable is in scope,
-- i.e. it has a binding in the given state
executeStatement (Assign id exp) fs ps state
= updateVar (id, val) state
where
val = eval exp fs state
executeStatement (AssignA id exp exp') fs ps state
= updateVar (id, assignArray arr index val) state
where
index = eval exp fs state
val = eval exp' fs state
(_, arr) = (lookUp id state)
executeStatement (If e b b') fs ps state
| bool == 1 = (executeBlock b fs ps state)
| otherwise = (executeBlock b' fs ps state)
where
(I bool) = eval e fs state
executeStatement (While e b) fs ps state
| bool == 1 = executeStatement (While e b) fs ps state'
| otherwise = state
where
(I bool) = eval e fs state
state' = executeBlock b fs ps state
executeStatement (Call to p es) fs ps state
| null to = resState
| otherwise = updateVar (to, resVal) resState
where
(ids, pBlock) = lookUp p ps
vals = evalArgs es fs state
pState = (bindArgs ids vals) ++ (getGlobals state)
state' = executeBlock pBlock fs ps pState
resState = (getLocals state) ++ (getGlobals state')
(_, resVal) = lookUp "$res" state'
executeStatement (Return exp) fs ps state
= updateVar ("$res", val) state
where
val = eval exp fs state
executeBlock :: Block -> [FunDef] -> [ProcDef] -> State -> State
-- Pre: All code blocks and associated statements are well formed
executeBlock b fs ps state
= foldl (\state' stmnt -> executeStatement stmnt fs ps state') state b
---------------------------------------------------------------------
-- Part IV
translate :: FunDef -> Id -> [(Id, Id)] -> ProcDef
translate (name, (as, e)) newName nameMap
= (newName, (as, b ++ [Return e']))
where
(b, e', ids') = translate' e nameMap ['$' : show n | n <- [1..]]
translate' :: Exp -> [(Id, Id)] -> [Id] -> (Block, Exp, [Id])
translate'
= undefined
---------------------------------------------------------------------
-- PREDEFINED FUNCTIONS
-- A helpful predefined lookUp function...
lookUp :: (Eq a, Show a) => a -> [(a, b)] -> b
lookUp x t
= fromMaybe (error ("\nAttempt to lookUp " ++ show x ++
" in a table that only has the bindings: " ++
show (map fst t)))
(lookup x t)
-- Turns an int into an Exp...
intToExp :: Int -> Exp
intToExp n
= Const (I n)
-- Turns a list of ints into an array Exp...
listToExp :: [Int] -> Exp
listToExp
= Const . listToVal
-- Turns a list of ints into an array Value...
listToVal :: [Int] -> Value
listToVal xs
= A (zip [0..] xs)
-- memoise generates a procedure that caches values computed by function f.
-- In general f will be a variant of some originally recursive function
-- that calls the procedure generated here (named p) instead of itself.
-- Arguments:
-- p = procedure name; a = argument name; f = function variant;
-- pt = 'isPresent' table; mt = memo table.
memoise :: Id -> Id -> Id -> Id -> Id -> ProcDef
memoise p a f pt mt
= (p,
([a], [If (OpApp Equal (OpApp Index (Var pt) (Var a)) (Const (I 0)))
[Call "x" f [Var a],
AssignA pt (Var a) (Const (I 1)),
AssignA mt (Var a) (Var "x")
]
[],
Return (OpApp Index (Var mt) (Var a))
]
)
)
---------------------------------------------------------------------
-- Predefined States, arrays and expressions for testing...
sampleState, gState, fibState :: State
sampleState
= [("x", (Local, I 5)), ("y", (Global, I 2)), ("a", (Global, listToVal [4,2,7]))]
gState
= [("gSum", (Global, I 0))]
fibState
= [("fibPres", (Global, A [])), ("fibTab", (Global, A []))]
sampleArray :: Exp
sampleArray
= Const (listToVal [9,5,7,1])
e1, e2, e3, e4, e5 :: Exp
e1 = Const (I 1)
e2 = Var "y"
e3 = OpApp Add (Var "x") (Const (I 2))
e4 = Cond e1 (Var "x") (Const (I 9))
e5 = FunApp "fib" [Const (I 6)]
---------------------------------------------------------------------
-- Example (pure) functions for testing...
-- Equivalent of Haskell's max function...
biggest :: FunDef
biggest
= ("biggest",
(["m", "n"], Cond (OpApp Less (Var "m") (Var "n"))
(Var "n")
(Var "m"))
)
-- Factorial, equivalent to: if n == 0 then 1 else n * fact (n - 1)...
fac :: FunDef
fac
= ("fac",
(["n"], Cond (OpApp Equal (Var "n") (intToExp 0))
(intToExp 1)
(OpApp Mul (Var "n")
(FunApp "fac" [OpApp Add (Var "n") (intToExp (-1))])))
)
-- Sums elements 0..n of an array...
sumA :: FunDef
sumA
= ("sumA",
(["a", "n"], Cond (OpApp Less (Var "n") (Const (I 0)))
(Const (I 0))
(OpApp Add (OpApp Index (Var "a") (Var "n"))
(FunApp "sumA"
[Var "a", OpApp Add (Var "n")
(Const (I (-1)))]))
)
)
-- Vanilla Haskell fib
fibH :: Int -> Int
-- Pre: n > 0
fibH n
= if n < 3 then 1 else fibH (n-1) + fibH (n-2)
-- fib in the purely functional subset
fib :: FunDef
fib
= ("fib",
(["n"], Cond (OpApp Less (Var "n") (Const (I 3)))
(Const (I 1))
(OpApp Add (FunApp "fib" [OpApp Add (Var "n") (Const (I (-1)))])
(FunApp "fib" [OpApp Add (Var "n") (Const (I (-2)))]))
)
)
-- May be useful for testing translate...?
testFun :: FunDef
testFun
= ("testFun",
(["x", "y"], Cond (OpApp Equal (Var "x") (Var "y"))
(Cond (FunApp "p" [Var "y"])
(OpApp Add (Var "x") (Const (I 1)))
(OpApp Add (Var "x") (Var "y")))
(OpApp Add (FunApp "g" [Var "y"]) (Const (I 2))))
)
---------------------------------------------------------------------
-- Example procedures for testing...
-- Add two integers and assign the result to a global variable, gSum,
-- that is assumed to be in scope when the procedure is called...
gAdd :: ProcDef
gAdd
= ("gAdd",
(["x", "y"], [Assign "gSum" (OpApp Add (Var "x") (Var "y"))])
)
-- Sums elements 0..n of an array...
sumA' :: ProcDef
sumA'
= ("sumA'",
(["a", "n"], [Assign "s" (Const (I 0)),
Assign "i" (Const (I 0)),
Assign "limit" (OpApp Add (Var "n") (Const (I 1))),
While (OpApp Less (Var "i") (Var "limit"))
[Assign "s" (OpApp Add (Var "s")
(OpApp Index (Var "a") (Var "i"))),
Assign "i" (OpApp Add (Var "i") (Const (I 1)))
],
Return (Var "s")]
)
)
-- A procedural version of fib...
fibP :: ProcDef
-- Pre: n > 0
fibP
= ("fibP",
(["n"], [If (OpApp Less (Var "n") (Const (I 3)))
[Return (Const (I 1))]
[Call "f1" "fibP" [OpApp Add (Var "n") (Const (I (-1)))],
Call "f2" "fibP" [OpApp Add (Var "n") (Const (I (-2)))],
Return (OpApp Add (Var "f1") (Var "f2"))
]
]
)
)
fibManager :: ProcDef
fibManager
= ("fibManager",
(["n"], [If (OpApp Equal (OpApp Index (Var "fibPres") (Var "n"))
(Const (I 0)))
[Call "x" "fibM" [Var "n"],
AssignA "fibPres" (Var "n") (Const (I 1)),
AssignA "fibTab" (Var "n") (Var "x")
]
[],
Return (OpApp Index (Var "fibTab") (Var "n"))
]
)
)
fibM :: ProcDef
-- Pre: n > 0
-- The value of fibMGenerator below
fibM
= ("fibM",
(["n"], [If (OpApp Less (Var "n") (Const (I 3)))
[Assign "$3" (Const (I 1))]
[Call "$1" "fibManager" [OpApp Add (Var "n") (Const (I (-1)))],
Call "$2" "fibManager" [OpApp Add (Var "n") (Const (I (-2)))],
Assign "$3" (OpApp Add (Var "$1") (Var "$2"))
],
Return (Var "$3")
]
)
)
---------------------------------------------------------------------
-- Sample top-level calls for testing...
-- This instantiates the table manager template (predefined)...
fibTableManager :: ProcDef
fibTableManager
= memoise "fibManager" "n" "fibM" "fibPres" "fibTab"
-- This uses the translate function to build the procedural, memoised,
-- version of fib...
fibMGenerator :: ProcDef
fibMGenerator
= translate fib "fibM" [("fib", "fibManager")]
-- Useful predefined executors...
execBiggest :: Int -> Int -> State
execBiggest m n
= executeBlock [Return (FunApp "biggest" [intToExp m, intToExp n])] [biggest] [] []
execFac :: Int -> State
execFac n
= executeBlock [Return (FunApp "fac" [intToExp n])] [fac] [] []
execSumA :: [Int] -> Int -> State
execSumA a n
= executeBlock [Return (FunApp "sumA" [listToExp a, intToExp n])] [sumA] [] []
execGAdd :: Int -> Int -> State
execGAdd x y
= executeBlock [Call "" "gAdd" [intToExp x, intToExp y]] [] [gAdd] gState
execSumA' :: [Int] -> Int -> State
execSumA' a n
= executeBlock [Call "s" "sumA'" [listToExp a, intToExp n]] [] [sumA'] []
execGlobalSumA' :: [Int] -> Int -> State
execGlobalSumA' a n
= executeBlock [Call "s" "sumA'" [listToExp a, intToExp n]]
[] [sumA'] [("s", (Global, I 0))]
execFibP :: Int -> State
execFibP n
= executeBlock [Call "f" "fibP" [intToExp n]] [] [fibP] fibState
execFibM :: Int -> State
execFibM n
= executeBlock [Call "f" "fibM" [intToExp n]] [] [fibM, fibManager] fibState