-- 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 Data where import Data.Array import CSG import Geometry import Illumination import Primitives import Surface import Debug.Trace -- Now the parsed (expresssion) language type Name = String type Code = [GMLToken] data GMLToken -- All these can occur in parsed code = TOp GMLOp | TId Name | TBind Name | TBool Bool | TInt Int | TReal Double | TString String | TBody Code | TArray Code | TApply | TIf -- These can occur in optimized/transformed code -- NONE (yet!) instance Show GMLToken where showsPrec p (TOp op) = shows op showsPrec p (TId id) = showString id showsPrec p (TBind id) = showString ('/' : id) showsPrec p (TBool bool) = shows bool showsPrec p (TInt i) = shows i showsPrec p (TReal d) = shows d showsPrec p (TString s) = shows s showsPrec p (TBody code) = shows code showsPrec p (TArray code) = showString "[ " . foldr (\ a b -> a . showChar ' ' . b) id (map shows code) . showString "]" showsPrec p (TApply) = showString "apply" showsPrec p (TIf) = showString "if" showList code = showString "{ " . foldr (\ a b -> a . showChar ' ' . b) id (map shows code) . showString "}" -- Now the value language, used inside the interpreter type Stack = [GMLValue] data GMLValue = VBool !Bool | VInt !Int | VReal !Double | VString String | VClosure Env Code | VArray (Array Int GMLValue) -- FIXME: Haskell array -- uses the interpreter version of point | VPoint { xPoint :: !Double , yPoint :: !Double , zPoint :: !Double } -- these are abstract to the interpreter | VObject Object | VLight Light -- This is an abstract object, used by the abstract interpreter | VAbsObj AbsObj -- There are only *3* basic abstract values, -- and the combinators also. data AbsObj = AbsFACE | AbsU | AbsV deriving (Show) instance Show GMLValue where showsPrec p value = showString (showStkEle value) showStkEle :: GMLValue -> String showStkEle (VBool b) = show b ++ " :: Bool" showStkEle (VInt i) = show i ++ " :: Int" showStkEle (VReal r) = show r ++ " :: Real" showStkEle (VString s) = show s ++ " :: String" showStkEle (VClosure {}) = " :: Closure" showStkEle (VArray arr) = " :: Array" showStkEle (VPoint x y z) = "(" ++ show x ++ "," ++ show y ++ "," ++ show z ++ ") :: Point" showStkEle (VObject {}) = " :: Object" showStkEle (VLight {}) = " :: Object" showStkEle (VAbsObj vobs) = "{{ " ++ show vobs ++ "}} :: AbsObj" -- An abstract environment newtype Env = Env [(Name, GMLValue)] deriving Show emptyEnv :: Env emptyEnv = Env [] extendEnv :: Env -> Name -> GMLValue -> Env extendEnv (Env e) n v = Env ((n, v):e) lookupEnv :: Env -> Name -> Maybe GMLValue lookupEnv (Env e) n = lookup n e -- All primitive operators -- -- There is no Op_apply, Op_false, Op_true and Op_if -- (because they appear explcitly in the rules). data GMLOp = Op_acos | Op_addi | Op_addf | Op_asin | Op_clampf | Op_cone | Op_cos | Op_cube | Op_cylinder | Op_difference | Op_divi | Op_divf | Op_eqi | Op_eqf | Op_floor | Op_frac | Op_get | Op_getx | Op_gety | Op_getz | Op_intersect | Op_length | Op_lessi | Op_lessf | Op_light | Op_modi | Op_muli | Op_mulf | Op_negi | Op_negf | Op_plane | Op_point | Op_pointlight | Op_real | Op_render | Op_rotatex | Op_rotatey | Op_rotatez | Op_scale | Op_sin | Op_sphere | Op_spotlight | Op_sqrt | Op_subi | Op_subf | Op_trace -- non standard, for debugging GML programs | Op_translate | Op_union | Op_uscale deriving (Eq,Ord,Ix,Bounded) instance Show GMLOp where showsPrec _ op = showString (opNameTable ! op) ------------------------------------------------------------------------------ -- And how we use the op codes (there names, there interface) -- These keywords include, "apply", "if", "true" and "false", -- they are not parsed as operators, but are -- captured by the parser as a special case. keyWords :: [String] keyWords = [ kwd | (kwd,_,_) <- opcodes ] -- Lookup has to look from the start (or else...) opTable :: [(Name,GMLToken)] opTable = [ (kwd,op) | (kwd,op,_) <- opcodes ] opNameTable :: Array GMLOp Name opNameTable = array (minBound,maxBound) [ (op,name) | (name,TOp op,_) <- opcodes ] undef = error "undefined function" image = error "undefined function: talk to image group" -- typically, its best to have *one* opcode table, -- so that mis-alignments do not happen. opcodes :: [(String,GMLToken,PrimOp)] opcodes = [ ("apply", TApply, error "incorrect use of apply") , ("if", TIf, error "incorrect use of if") , ("false", TBool False, error "incorrect use of false") , ("true", TBool True, error "incorrect use of true") ] ++ map (\ (a,b,c) -> (a,TOp b,c)) -- These are just invocation, any coersions need to occur between here -- and before arriving at the application code (like deg -> rad). [ ("acos", Op_acos, Real_Real (rad2deg . acos)) , ("addi", Op_addi, Int_Int_Int (+)) , ("addf", Op_addf, Real_Real_Real (+)) , ("asin", Op_asin, Real_Real (rad2deg . asin)) , ("clampf", Op_clampf, Real_Real clampf) , ("cone", Op_cone, Surface_Obj cone) , ("cos", Op_cos, Real_Real (cos . deg2rad)) , ("cube", Op_cube, Surface_Obj cube) , ("cylinder", Op_cylinder, Surface_Obj cylinder) , ("difference", Op_difference, Obj_Obj_Obj difference) , ("divi", Op_divi, Int_Int_Int (ourQuot)) , ("divf", Op_divf, Real_Real_Real (/)) , ("eqi", Op_eqi, Int_Int_Bool (==)) , ("eqf", Op_eqf, Real_Real_Bool (==)) , ("floor", Op_floor, Real_Int floor) , ("frac", Op_frac, Real_Real (snd . properFraction)) , ("get", Op_get, Arr_Int_Value ixGet) , ("getx", Op_getx, Point_Real (\ x y z -> x)) , ("gety", Op_gety, Point_Real (\ x y z -> y)) , ("getz", Op_getz, Point_Real (\ x y z -> z)) , ("intersect", Op_intersect, Obj_Obj_Obj intersect) , ("length", Op_length, Arr_Int (succ . snd . bounds)) , ("lessi", Op_lessi, Int_Int_Bool (<)) , ("lessf", Op_lessf, Real_Real_Bool (<)) , ("light", Op_light, Point_Color_Light light) , ("modi", Op_modi, Int_Int_Int (ourRem)) , ("muli", Op_muli, Int_Int_Int (*)) , ("mulf", Op_mulf, Real_Real_Real (*)) , ("negi", Op_negi, Int_Int negate) , ("negf", Op_negf, Real_Real negate) , ("plane", Op_plane, Surface_Obj plane) , ("point", Op_point, Real_Real_Real_Point VPoint) , ("pointlight", Op_pointlight, Point_Color_Light pointlight) , ("real", Op_real, Int_Real fromIntegral) , ("render", Op_render, Render $ render eye) , ("rotatex", Op_rotatex, Obj_Real_Obj (\ o d -> rotateX (deg2rad d) o)) , ("rotatey", Op_rotatey, Obj_Real_Obj (\ o d -> rotateY (deg2rad d) o)) , ("rotatez", Op_rotatez, Obj_Real_Obj (\ o d -> rotateZ (deg2rad d) o)) , ("scale", Op_scale, Obj_Real_Real_Real_Obj (\ o x y z -> scale (x,y,z) o)) , ("sin", Op_sin, Real_Real (sin . deg2rad)) , ("sphere", Op_sphere, Surface_Obj sphere') -- see comment at end of file , ("spotlight", Op_spotlight, Point_Point_Color_Real_Real_Light mySpotlight) , ("sqrt", Op_sqrt, Real_Real ourSqrt) , ("subi", Op_subi, Int_Int_Int (-)) , ("subf", Op_subf, Real_Real_Real (-)) , ("trace", Op_trace, Value_String_Value mytrace) , ("translate", Op_translate, Obj_Real_Real_Real_Obj (\ o x y z -> translate (x,y,z) o)) , ("union", Op_union, Obj_Obj_Obj union) , ("uscale", Op_uscale, Obj_Real_Obj (\ o r -> uscale r o)) ] -- This enumerate all possible ways of calling the fixed primitives -- The datatype captures the type at the *interp* level, -- the type of the functional is mirrored on this (using Haskell types). data PrimOp -- 1 argument = Int_Int (Int -> Int) | Real_Real (Double -> Double) | Point_Real (Double -> Double -> Double -> Double) | Surface_Obj (SurfaceFn Color Double -> Object) | Real_Int (Double -> Int) | Int_Real (Int -> Double) | Arr_Int (Array Int GMLValue -> Int) -- 2 arguments | Int_Int_Int (Int -> Int -> Int) | Int_Int_Bool (Int -> Int -> Bool) | Real_Real_Real (Double -> Double -> Double) | Real_Real_Bool (Double -> Double -> Bool) | Arr_Int_Value (Array Int GMLValue -> Int -> GMLValue) -- Many arguments, typically image mangling | Obj_Obj_Obj (Object -> Object -> Object) | Point_Color_Light (Coords -> Color -> Light) | Real_Real_Real_Point (Double -> Double -> Double -> GMLValue) | Obj_Real_Obj (Object -> Double -> Object) | Obj_Real_Real_Real_Obj (Object -> Double -> Double -> Double -> Object) | Value_String_Value (GMLValue -> String -> GMLValue) | Point_Point_Color_Real_Real_Light (Coords -> Coords -> Color -> Radian -> Radian -> Light) -- And finally render | Render (Color -> [Light] -> Object -> Int -> Double -> Int -> Int -> String -> IO ()) data Type = TyBool | TyInt | TyReal | TyString | TyCode | TyArray | TyPoint | TyObject | TyLight | TyAlpha | TyAbsObj deriving (Eq,Ord,Ix,Bounded) typeTable = [ ( TyBool, "Bool") , ( TyInt, "Int") , ( TyReal, "Real") , ( TyString, "String") , ( TyCode, "Code") , ( TyArray, "Array") , ( TyPoint, "Point") , ( TyObject, "Object") , ( TyLight, "Light") , ( TyAlpha, "") , ( TyAbsObj, "") ] typeNames = array (minBound,maxBound) typeTable instance Show Type where showsPrec _ op = showString (typeNames ! op) getPrimOpType :: PrimOp -> [Type] getPrimOpType (Int_Int _) = [TyInt] getPrimOpType (Real_Real _) = [TyReal] getPrimOpType (Point_Real _) = [TyPoint] getPrimOpType (Surface_Obj _) = [TyCode] getPrimOpType (Real_Int _) = [TyReal] getPrimOpType (Int_Real _) = [TyInt] getPrimOpType (Arr_Int _) = [TyArray] getPrimOpType (Int_Int_Int _) = [TyInt,TyInt] getPrimOpType (Int_Int_Bool _) = [TyInt,TyInt] getPrimOpType (Real_Real_Real _) = [TyReal,TyReal] getPrimOpType (Real_Real_Bool _) = [TyReal,TyReal] getPrimOpType (Arr_Int_Value _) = [TyArray,TyInt] getPrimOpType (Obj_Obj_Obj _) = [TyObject,TyObject] getPrimOpType (Point_Color_Light _) = [TyPoint,TyPoint] getPrimOpType (Real_Real_Real_Point _) = [TyReal,TyReal,TyReal] getPrimOpType (Obj_Real_Obj _) = [TyObject,TyReal] getPrimOpType (Obj_Real_Real_Real_Obj _) = [TyObject,TyReal,TyReal,TyReal] getPrimOpType (Value_String_Value _) = [TyAlpha,TyString] getPrimOpType (Point_Point_Color_Real_Real_Light _) = [TyPoint,TyPoint,TyPoint,TyReal,TyReal] getPrimOpType (Render _) = [TyPoint, TyLight, TyObject, TyInt, TyReal, TyReal, TyReal, TyString] -- Some primitives with better error message mytrace v s = trace (s ++" : "++ show v ++ "\n") v ixGet :: Array Int GMLValue -> Int -> GMLValue ixGet arr i | inRange (bounds arr) i = arr ! i | otherwise = error ("failed access with index value " ++ show i ++ " (should be between 0 and " ++ show (snd (bounds arr)) ++ ")") ourQuot :: Int -> Int -> Int ourQuot _ 0 = error "attempt to use divi to divide by 0" ourQuot a b = a `quot` b ourRem :: Int -> Int -> Int ourRem _ 0 = error "attempt to use remi to divide by 0" ourRem a b = a `rem` b ourSqrt :: Double -> Double ourSqrt n | n < 0 = error "attempt to use sqrt on a negative number" | otherwise = sqrt n mySpotlight p1 p2 col cutoff exp = spotlight p1 p2 col (deg2rad cutoff) exp -- The problem specification gets the mapping for spheres backwards -- (it maps the image from right to left). -- We've fixed that in the raytracing library so that it goes from left -- to right, but to keep the GML front compatible with the problem -- statement, we reverse it here. sphere' :: SurfaceFn Color Double -> CSG (SurfaceFn Color Double) sphere' (SFun f) = sphere (SFun (\i u v -> f i (1 - u) v)) sphere' s = sphere s