1 -- Copyright (c) 2000 Galois Connections, Inc.
2 -- All rights reserved. This software is distributed as
3 -- free software under the license in the file "LICENSE",
4 -- which is included in the distribution.
16 import Parse (rayParse, rayParseF)
18 class Monad m => MonadEval m where
19 doOp :: PrimOp -> GMLOp -> Stack -> m Stack
25 newtype Pure a = Pure a deriving Show
27 instance Monad Pure where
32 instance MonadEval Pure where
36 instance MonadEval IO where
37 doOp prim op stk = do { -- putStrLn ("Calling " ++ show op
38 -- ++ " << " ++ show stk ++ " >>")
49 callback :: Env -> Code -> Stack -> Stack
51 = case eval (State { env = env, stack = stk, code = code}) of
54 {-# SPECIALIZE eval :: State -> Pure Stack #-}
55 {-# SPECIALIZE eval :: State -> IO Stack #-}
57 eval :: MonadEval m => State -> m Stack
59 do { () <- return () -- $ unsafePerformIO (print st) -- Functional debugger
61 do { tick -- tick first, so as to catch loops on new eval.
65 else return (stack st)
68 moreCode :: State -> Bool
69 moreCode (State {code = []}) = False
72 -- Step has a precondition that there *is* code to run
73 {-# SPECIALIZE step :: State -> Pure State #-}
74 {-# SPECIALIZE step :: State -> IO State #-}
75 step :: MonadEval m => State -> m State
77 -- Rule 1: Pushing BaseValues
78 step st@(State{ stack = stack, code = (TBool b):cs })
79 = return (st { stack = (VBool b):stack, code = cs })
80 step st@(State{ stack = stack, code = (TInt i):cs })
81 = return (st { stack = (VInt i):stack, code = cs })
82 step st@(State{ stack = stack, code = (TReal r):cs })
83 = return (st { stack = (VReal r):stack, code = cs })
84 step st@(State{ stack = stack, code = (TString s):cs })
85 = return (st { stack = (VString s):stack, code = cs })
87 -- Rule 2: Name binding
88 step st@(State{ env = env, stack = (v:stack), code = (TBind id):cs }) =
89 return (State { env = extendEnv env id v, stack = stack, code = cs })
90 step st@(State{ env = env, stack = [], code = (TBind id):cs }) =
91 err "Attempt to bind the top of an empty stack"
93 -- Rule 3: Name lookup
94 step st@(State{ env = env, stack = stack, code = (TId id):cs }) =
95 case (lookupEnv env id) of
96 Just v -> return (st { stack = v:stack, code = cs })
97 Nothing -> err ("Cannot find value for identifier: " ++ id)
99 -- Rule 4: Closure creation
100 step st@(State{ env = env, stack = stack, code = (TBody body):cs }) =
101 return (st { stack = (VClosure env body):stack, code = cs })
103 -- Rule 5: Application
104 step st@(State{ env = env, stack = (VClosure env' code'):stack, code = TApply:cs }) =
105 do { stk <- eval (State {env = env', stack = stack, code = code'})
106 ; return (st { stack = stk, code = cs })
108 step st@(State{ env = env, stack = [], code = TApply:cs }) =
109 err "Application with an empty stack"
110 step st@(State{ env = env, stack = _:_, code = TApply:cs }) =
111 err "Application of a non-closure"
114 step st@(State{ env = env, stack = stack, code = TArray code':cs }) =
115 do { stk <- eval (State {env = env, stack = [], code = code'})
116 ; let last = length stk-1
117 ; let arr = array (0,last) (zip [last,last-1..] stk)
118 ; return (st { stack = (VArray arr):stack, code = cs })
121 -- Rule 7 & 8: If statement
122 step st@(State{ env = env, stack = (VClosure e2 c2):(VClosure e1 c1):(VBool True):stack, code = TIf:cs }) =
123 do { stk <- eval (State {env = e1, stack = stack, code = c1})
124 ; return (st { stack = stk, code = cs })
126 step st@(State{ env = env, stack = (VClosure e2 c2):(VClosure e1 c1):(VBool False):stack, code = TIf:cs }) =
127 do { stk <- eval (State {env = e2, stack = stack, code = c2})
128 ; return (st { stack = stk, code = cs })
130 step st@(State{ env = env, stack = _, code = TIf:cs }) =
131 err "Incorrect use of if (bad and/or inappropriate values on the stack)"
134 step st@(State{ env = env, stack = stack, code = (TOp op):cs }) =
135 do { stk <- doOp (opFnTable ! op) op stack
136 ; return (st { stack = stk, code = cs })
140 step _ = err "Tripped on sidewalk while stepping."
143 --------------------------------------------------------------------------
146 opFnTable :: Array GMLOp PrimOp
147 opFnTable = array (minBound,maxBound)
148 [ (op,prim) | (_,TOp op,prim) <- opcodes ]
153 doPureOp :: (MonadEval m) => PrimOp -> GMLOp -> Stack -> m Stack
154 doPureOp _ Op_render _ =
155 err ("\nAttempting to call render from inside a purely functional callback.")
156 doPureOp primOp op stk = doPrimOp primOp op stk -- call the purely functional operators
158 {-# SPECIALIZE doPrimOp :: PrimOp -> GMLOp -> Stack -> Pure Stack #-}
159 {-# SPECIALIZE doPrimOp :: PrimOp -> GMLOp -> Stack -> IO Stack #-}
160 {-# SPECIALIZE doPrimOp :: PrimOp -> GMLOp -> Stack -> Abs Stack #-}
162 doPrimOp :: (MonadEval m) => PrimOp -> GMLOp -> Stack -> m Stack
166 doPrimOp (Int_Int fn) _ (VInt i1:stk)
167 = return ((VInt (fn i1)) : stk)
168 doPrimOp (Real_Real fn) _ (VReal r1:stk)
169 = return ((VReal (fn r1)) : stk)
170 doPrimOp (Point_Real fn) _ (VPoint x y z:stk)
171 = return ((VReal (fn x y z)) : stk)
173 -- This is where the callbacks happen from...
174 doPrimOp (Surface_Obj fn) _ (VClosure env code:stk)
175 = case absapply env code [VAbsObj AbsFACE,VAbsObj AbsU,VAbsObj AbsV] of
176 Just [VReal r3,VReal r2,VReal r1,VPoint c1 c2 c3] ->
178 res = prop (color c1 c2 c3) r1 r2 r3
180 return ((VObject (fn (SConst res))) : stk)
181 _ -> return ((VObject (fn (SFun call))) : stk)
183 -- The most general case
185 case callback env code [VReal r2,VReal r1,VInt i] of
186 [VReal r3,VReal r2,VReal r1,VPoint c1 c2 c3]
187 -> prop (color c1 c2 c3) r1 r2 r3
188 stk -> error ("callback failed: incorrectly typed return arguments"
191 doPrimOp (Real_Int fn) _ (VReal r1:stk)
192 = return ((VInt (fn r1)) : stk)
193 doPrimOp (Int_Real fn) _ (VInt r1:stk)
194 = return ((VReal (fn r1)) : stk)
195 doPrimOp (Arr_Int fn) _ (VArray arr:stk)
196 = return ((VInt (fn arr)) : stk)
200 doPrimOp (Int_Int_Int fn) _ (VInt i2:VInt i1:stk)
201 = return ((VInt (fn i1 i2)) : stk)
202 doPrimOp (Int_Int_Bool fn) _ (VInt i2:VInt i1:stk)
203 = return ((VBool (fn i1 i2)) : stk)
204 doPrimOp (Real_Real_Real fn) _ (VReal r2:VReal r1:stk)
205 = return ((VReal (fn r1 r2)) : stk)
206 doPrimOp (Real_Real_Bool fn) _ (VReal r2:VReal r1:stk)
207 = return ((VBool (fn r1 r2)) : stk)
208 doPrimOp (Arr_Int_Value fn) _ (VInt i:VArray arr:stk)
209 = return ((fn arr i) : stk)
212 -- Many arguments, typically image mangling
214 doPrimOp (Obj_Obj_Obj fn) _ (VObject o2:VObject o1:stk)
215 = return ((VObject (fn o1 o2)) : stk)
216 doPrimOp (Point_Color_Light fn) _ (VPoint r g b:VPoint x y z : stk)
217 = return (VLight (fn (x,y,z) (color r g b)) : stk)
218 doPrimOp (Point_Point_Color_Real_Real_Light fn) _
219 (VReal r2:VReal r1:VPoint r g b:VPoint x2 y2 z2:VPoint x1 y1 z1 : stk)
220 = return (VLight (fn (x1,y1,z1) (x2,y2,z2) (color r g b) r1 r2) : stk)
221 doPrimOp (Real_Real_Real_Point fn) _ (VReal r3:VReal r2:VReal r1:stk)
222 = return ((fn r1 r2 r3) : stk)
223 doPrimOp (Obj_Real_Obj fn) _ (VReal r:VObject o:stk)
224 = return (VObject (fn o r) : stk)
225 doPrimOp (Obj_Real_Real_Real_Obj fn) _ (VReal r3:VReal r2:VReal r1:VObject o:stk)
226 = return (VObject (fn o r1 r2 r3) : stk)
228 -- This one is our testing harness
229 doPrimOp (Value_String_Value fn) _ (VString s:o:stk)
230 = res `seq` return (res : stk)
234 doPrimOp primOp op args
235 = err ("\n\ntype error when attempting to execute builtin primitive \"" ++
236 show op ++ "\"\n\n| " ++
237 show op ++ " takes " ++ show (length types) ++ " argument" ++ s
238 ++ " with" ++ the ++ " type" ++ s ++ "\n|\n|" ++
239 " " ++ unwords [ show ty | ty <- types ] ++ "\n|\n|" ++
240 " currently, the relevent argument" ++ s ++ " on the stack " ++
242 unwords [ "(" ++ show arg ++ ")"
243 | arg <- reverse (take (length types) args) ] ++ "\n|\n| "
244 ++ " (top of stack is on the right hand side)\n\n")
247 s = (if len /= 1 then "s" else "")
248 are = (if len /= 1 then "are" else "is")
249 the = (if len /= 1 then "" else " the")
250 types = getPrimOpType primOp
253 -- Render is somewhat funny, becauase it can only get called at top level.
254 -- All other operations are purely functional.
256 doAllOp :: PrimOp -> GMLOp -> Stack -> IO Stack
257 doAllOp (Render render) Op_render
258 (VString str:VInt ht:VInt wid:VReal fov
259 :VInt dep:VObject obj:VArray arr
261 = do { render (color r g b) lights obj dep (fov * (pi / 180.0)) wid ht str
265 lights = [ light | (VLight light) <- elems arr ]
267 doAllOp primOp op stk = doPrimOp primOp op stk -- call the purely functional operators
269 ------------------------------------------------------------------------------
271 - Abstract evaluation.
273 - The idea is you check for constant code that
274 - (1) does not look at its arguments
275 - (2) gives a fixed result
277 - We run for 100 steps.
281 absapply :: Env -> Code -> Stack -> Maybe Stack
282 absapply env code stk =
283 case runAbs (eval (State env stk code)) 100 of
284 AbsState stk _ -> Just stk
287 newtype Abs a = Abs { runAbs :: Int -> AbsState a }
288 data AbsState a = AbsState a !Int
291 instance Monad Abs where
292 (Abs fn) >>= k = Abs (\ s -> case fn s of
293 AbsState r s' -> runAbs (k r) s'
294 AbsFail m -> AbsFail m)
295 return x = Abs (\ n -> AbsState x n)
296 fail s = Abs (\ n -> AbsFail s)
298 instance MonadEval Abs where
301 tick = Abs (\ n -> if n <= 0
302 then AbsFail "run out of time"
303 else AbsState () (n-1))
305 doAbsOp :: PrimOp -> GMLOp -> Stack -> Abs Stack
306 doAbsOp _ Op_point (VReal r3:VReal r2:VReal r1:stk)
307 = return ((VPoint r1 r2 r3) : stk)
308 -- here, you could have an (AbsPoint :: AbsObj) which you put on the
309 -- stack, with any object in the three fields.
310 doAbsOp _ op _ = err ("operator not understood (" ++ show op ++ ")")
312 ------------------------------------------------------------------------------
315 mainEval :: Code -> IO ()
316 mainEval prog = do { stk <- eval (State emptyEnv [] prog)
320 * Oops, one of the example actually has something
321 * on the stack at the end.
325 else do { putStrLn done
330 done = "Items still on stack at (successfull) termination of program"
332 ------------------------------------------------------------------------------
335 test :: String -> Pure Stack
336 test is = eval (State emptyEnv [] (rayParse is))
338 testF :: String -> IO Stack
339 testF is = do prog <- rayParseF is
340 eval (State emptyEnv [] prog)
342 testA :: String -> Either String (Stack,Int)
343 testA is = case runAbs (eval (State emptyEnv
344 [VAbsObj AbsFACE,VAbsObj AbsU,VAbsObj AbsV]
345 (rayParse is))) 100 of
346 AbsState a n -> Right (a,n)
349 abstest1 = "1.0 0.0 0.0 point /red { /v /u /face red 1.0 0.0 1.0 } apply"
351 -- should be [3:: Int]
352 et1 = test "1 /x { x } /f 2 /x f apply x addi"