+++ /dev/null
--- Copyright (c) 2000 Galois Connections, Inc.
--- All rights reserved. This software is distributed as
--- free software under the license in the file "LICENSE",
--- which is included in the distribution.
-
-module Eval where
-
-import Array
-
-import IOExts
-
-import Geometry
-import CSG
-import Surface
-import Data
-import Parse (rayParse, rayParseF)
-
-class Monad m => MonadEval m where
- doOp :: PrimOp -> GMLOp -> Stack -> m Stack
- tick :: m ()
- err :: String -> m a
-
- tick = return ()
-
-newtype Pure a = Pure a deriving Show
-
-instance Monad Pure where
- Pure x >>= k = k x
- return = Pure
- fail s = error s
-
-instance MonadEval Pure where
- doOp = doPureOp
- err s = error s
-
-instance MonadEval IO where
- doOp prim op stk = do { -- putStrLn ("Calling " ++ show op
- -- ++ " << " ++ show stk ++ " >>")
- doAllOp prim op stk
- }
- err s = error s
-
-data State
- = State { env :: Env
- , stack :: Stack
- , code :: Code
- } deriving Show
-
-callback :: Env -> Code -> Stack -> Stack
-callback env code stk
- = case eval (State { env = env, stack = stk, code = code}) of
- Pure stk -> stk
-
-{-# SPECIALIZE eval :: State -> Pure Stack #-}
-{-# SPECIALIZE eval :: State -> IO Stack #-}
-
-eval :: MonadEval m => State -> m Stack
-eval st =
- do { () <- return () -- $ unsafePerformIO (print st) -- Functional debugger
- ; if moreCode st then
- do { tick -- tick first, so as to catch loops on new eval.
- ; st' <- step st
- ; eval st'
- }
- else return (stack st)
- }
-
-moreCode :: State -> Bool
-moreCode (State {code = []}) = False
-moreCode _ = True
-
--- Step has a precondition that there *is* code to run
-{-# SPECIALIZE step :: State -> Pure State #-}
-{-# SPECIALIZE step :: State -> IO State #-}
-step :: MonadEval m => State -> m State
-
--- Rule 1: Pushing BaseValues
-step st@(State{ stack = stack, code = (TBool b):cs })
- = return (st { stack = (VBool b):stack, code = cs })
-step st@(State{ stack = stack, code = (TInt i):cs })
- = return (st { stack = (VInt i):stack, code = cs })
-step st@(State{ stack = stack, code = (TReal r):cs })
- = return (st { stack = (VReal r):stack, code = cs })
-step st@(State{ stack = stack, code = (TString s):cs })
- = return (st { stack = (VString s):stack, code = cs })
-
--- Rule 2: Name binding
-step st@(State{ env = env, stack = (v:stack), code = (TBind id):cs }) =
- return (State { env = extendEnv env id v, stack = stack, code = cs })
-step st@(State{ env = env, stack = [], code = (TBind id):cs }) =
- err "Attempt to bind the top of an empty stack"
-
--- Rule 3: Name lookup
-step st@(State{ env = env, stack = stack, code = (TId id):cs }) =
- case (lookupEnv env id) of
- Just v -> return (st { stack = v:stack, code = cs })
- Nothing -> err ("Cannot find value for identifier: " ++ id)
-
--- Rule 4: Closure creation
-step st@(State{ env = env, stack = stack, code = (TBody body):cs }) =
- return (st { stack = (VClosure env body):stack, code = cs })
-
--- Rule 5: Application
-step st@(State{ env = env, stack = (VClosure env' code'):stack, code = TApply:cs }) =
- do { stk <- eval (State {env = env', stack = stack, code = code'})
- ; return (st { stack = stk, code = cs })
- }
-step st@(State{ env = env, stack = [], code = TApply:cs }) =
- err "Application with an empty stack"
-step st@(State{ env = env, stack = _:_, code = TApply:cs }) =
- err "Application of a non-closure"
-
--- Rule 6: Arrays
-step st@(State{ env = env, stack = stack, code = TArray code':cs }) =
- do { stk <- eval (State {env = env, stack = [], code = code'})
- ; let last = length stk-1
- ; let arr = array (0,last) (zip [last,last-1..] stk)
- ; return (st { stack = (VArray arr):stack, code = cs })
- }
-
--- Rule 7 & 8: If statement
-step st@(State{ env = env, stack = (VClosure e2 c2):(VClosure e1 c1):(VBool True):stack, code = TIf:cs }) =
- do { stk <- eval (State {env = e1, stack = stack, code = c1})
- ; return (st { stack = stk, code = cs })
- }
-step st@(State{ env = env, stack = (VClosure e2 c2):(VClosure e1 c1):(VBool False):stack, code = TIf:cs }) =
- do { stk <- eval (State {env = e2, stack = stack, code = c2})
- ; return (st { stack = stk, code = cs })
- }
-step st@(State{ env = env, stack = _, code = TIf:cs }) =
- err "Incorrect use of if (bad and/or inappropriate values on the stack)"
-
--- Rule 9: Operators
-step st@(State{ env = env, stack = stack, code = (TOp op):cs }) =
- do { stk <- doOp (opFnTable ! op) op stack
- ; return (st { stack = stk, code = cs })
- }
-
--- Rule Opps
-step _ = err "Tripped on sidewalk while stepping."
-
-
---------------------------------------------------------------------------
--- Operator code
-
-opFnTable :: Array GMLOp PrimOp
-opFnTable = array (minBound,maxBound)
- [ (op,prim) | (_,TOp op,prim) <- opcodes ]
-
-
-
-
-doPureOp :: (MonadEval m) => PrimOp -> GMLOp -> Stack -> m Stack
-doPureOp _ Op_render _ =
- err ("\nAttempting to call render from inside a purely functional callback.")
-doPureOp primOp op stk = doPrimOp primOp op stk -- call the purely functional operators
-
-{-# SPECIALIZE doPrimOp :: PrimOp -> GMLOp -> Stack -> Pure Stack #-}
-{-# SPECIALIZE doPrimOp :: PrimOp -> GMLOp -> Stack -> IO Stack #-}
-{-# SPECIALIZE doPrimOp :: PrimOp -> GMLOp -> Stack -> Abs Stack #-}
-
-doPrimOp :: (MonadEval m) => PrimOp -> GMLOp -> Stack -> m Stack
-
--- 1 argument.
-
-doPrimOp (Int_Int fn) _ (VInt i1:stk)
- = return ((VInt (fn i1)) : stk)
-doPrimOp (Real_Real fn) _ (VReal r1:stk)
- = return ((VReal (fn r1)) : stk)
-doPrimOp (Point_Real fn) _ (VPoint x y z:stk)
- = return ((VReal (fn x y z)) : stk)
-
--- This is where the callbacks happen from...
-doPrimOp (Surface_Obj fn) _ (VClosure env code:stk)
- = case absapply env code [VAbsObj AbsFACE,VAbsObj AbsU,VAbsObj AbsV] of
- Just [VReal r3,VReal r2,VReal r1,VPoint c1 c2 c3] ->
- let
- res = prop (color c1 c2 c3) r1 r2 r3
- in
- return ((VObject (fn (SConst res))) : stk)
- _ -> return ((VObject (fn (SFun call))) : stk)
- where
- -- The most general case
- call i r1 r2 =
- case callback env code [VReal r2,VReal r1,VInt i] of
- [VReal r3,VReal r2,VReal r1,VPoint c1 c2 c3]
- -> prop (color c1 c2 c3) r1 r2 r3
- stk -> error ("callback failed: incorrectly typed return arguments"
- ++ show stk)
-
-doPrimOp (Real_Int fn) _ (VReal r1:stk)
- = return ((VInt (fn r1)) : stk)
-doPrimOp (Int_Real fn) _ (VInt r1:stk)
- = return ((VReal (fn r1)) : stk)
-doPrimOp (Arr_Int fn) _ (VArray arr:stk)
- = return ((VInt (fn arr)) : stk)
-
--- 2 arguments.
-
-doPrimOp (Int_Int_Int fn) _ (VInt i2:VInt i1:stk)
- = return ((VInt (fn i1 i2)) : stk)
-doPrimOp (Int_Int_Bool fn) _ (VInt i2:VInt i1:stk)
- = return ((VBool (fn i1 i2)) : stk)
-doPrimOp (Real_Real_Real fn) _ (VReal r2:VReal r1:stk)
- = return ((VReal (fn r1 r2)) : stk)
-doPrimOp (Real_Real_Bool fn) _ (VReal r2:VReal r1:stk)
- = return ((VBool (fn r1 r2)) : stk)
-doPrimOp (Arr_Int_Value fn) _ (VInt i:VArray arr:stk)
- = return ((fn arr i) : stk)
-
-
- -- Many arguments, typically image mangling
-
-doPrimOp (Obj_Obj_Obj fn) _ (VObject o2:VObject o1:stk)
- = return ((VObject (fn o1 o2)) : stk)
-doPrimOp (Point_Color_Light fn) _ (VPoint r g b:VPoint x y z : stk)
- = return (VLight (fn (x,y,z) (color r g b)) : stk)
-doPrimOp (Point_Point_Color_Real_Real_Light fn) _
- (VReal r2:VReal r1:VPoint r g b:VPoint x2 y2 z2:VPoint x1 y1 z1 : stk)
- = return (VLight (fn (x1,y1,z1) (x2,y2,z2) (color r g b) r1 r2) : stk)
-doPrimOp (Real_Real_Real_Point fn) _ (VReal r3:VReal r2:VReal r1:stk)
- = return ((fn r1 r2 r3) : stk)
-doPrimOp (Obj_Real_Obj fn) _ (VReal r:VObject o:stk)
- = return (VObject (fn o r) : stk)
-doPrimOp (Obj_Real_Real_Real_Obj fn) _ (VReal r3:VReal r2:VReal r1:VObject o:stk)
- = return (VObject (fn o r1 r2 r3) : stk)
-
--- This one is our testing harness
-doPrimOp (Value_String_Value fn) _ (VString s:o:stk)
- = res `seq` return (res : stk)
- where
- res = fn o s
-
-doPrimOp primOp op args
- = err ("\n\ntype error when attempting to execute builtin primitive \"" ++
- show op ++ "\"\n\n| " ++
- show op ++ " takes " ++ show (length types) ++ " argument" ++ s
- ++ " with" ++ the ++ " type" ++ s ++ "\n|\n|" ++
- " " ++ unwords [ show ty | ty <- types ] ++ "\n|\n|" ++
- " currently, the relevent argument" ++ s ++ " on the stack " ++
- are ++ "\n|\n| " ++
- unwords [ "(" ++ show arg ++ ")"
- | arg <- reverse (take (length types) args) ] ++ "\n|\n| "
- ++ " (top of stack is on the right hand side)\n\n")
- where
- len = length types
- s = (if len /= 1 then "s" else "")
- are = (if len /= 1 then "are" else "is")
- the = (if len /= 1 then "" else " the")
- types = getPrimOpType primOp
-
-
--- Render is somewhat funny, becauase it can only get called at top level.
--- All other operations are purely functional.
-
-doAllOp :: PrimOp -> GMLOp -> Stack -> IO Stack
-doAllOp (Render render) Op_render
- (VString str:VInt ht:VInt wid:VReal fov
- :VInt dep:VObject obj:VArray arr
- :VPoint r g b : stk)
- = do { render (color r g b) lights obj dep (fov * (pi / 180.0)) wid ht str
- ; return stk
- }
- where
- lights = [ light | (VLight light) <- elems arr ]
-
-doAllOp primOp op stk = doPrimOp primOp op stk -- call the purely functional operators
-
-------------------------------------------------------------------------------
-{-
- - Abstract evaluation.
- -
- - The idea is you check for constant code that
- - (1) does not look at its arguments
- - (2) gives a fixed result
- -
- - We run for 100 steps.
- -
- -}
-
-absapply :: Env -> Code -> Stack -> Maybe Stack
-absapply env code stk =
- case runAbs (eval (State env stk code)) 100 of
- AbsState stk _ -> Just stk
- AbsFail m -> Nothing
-
-newtype Abs a = Abs { runAbs :: Int -> AbsState a }
-data AbsState a = AbsState a !Int
- | AbsFail String
-
-instance Monad Abs where
- (Abs fn) >>= k = Abs (\ s -> case fn s of
- AbsState r s' -> runAbs (k r) s'
- AbsFail m -> AbsFail m)
- return x = Abs (\ n -> AbsState x n)
- fail s = Abs (\ n -> AbsFail s)
-
-instance MonadEval Abs where
- doOp = doAbsOp
- err = fail
- tick = Abs (\ n -> if n <= 0
- then AbsFail "run out of time"
- else AbsState () (n-1))
-
-doAbsOp :: PrimOp -> GMLOp -> Stack -> Abs Stack
-doAbsOp _ Op_point (VReal r3:VReal r2:VReal r1:stk)
- = return ((VPoint r1 r2 r3) : stk)
- -- here, you could have an (AbsPoint :: AbsObj) which you put on the
- -- stack, with any object in the three fields.
-doAbsOp _ op _ = err ("operator not understood (" ++ show op ++ ")")
-
-------------------------------------------------------------------------------
--- Driver
-
-mainEval :: Code -> IO ()
-mainEval prog = do { stk <- eval (State emptyEnv [] prog)
- ; return ()
- }
-{-
- * Oops, one of the example actually has something
- * on the stack at the end.
- * Oh well...
- ; if null stk
- then return ()
- else do { putStrLn done
- ; print stk
- }
--}
-
-done = "Items still on stack at (successfull) termination of program"
-
-------------------------------------------------------------------------------
--- testing
-
-test :: String -> Pure Stack
-test is = eval (State emptyEnv [] (rayParse is))
-
-testF :: String -> IO Stack
-testF is = do prog <- rayParseF is
- eval (State emptyEnv [] prog)
-
-testA :: String -> Either String (Stack,Int)
-testA is = case runAbs (eval (State emptyEnv
- [VAbsObj AbsFACE,VAbsObj AbsU,VAbsObj AbsV]
- (rayParse is))) 100 of
- AbsState a n -> Right (a,n)
- AbsFail m -> Left m
-
-abstest1 = "1.0 0.0 0.0 point /red { /v /u /face red 1.0 0.0 1.0 } apply"
-
--- should be [3:: Int]
-et1 = test "1 /x { x } /f 2 /x f apply x addi"
-
-
-
-
-