implicit-0.0.2: Graphics/Implicit/ExtOpenScad/Definitions.hs
-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca)
-- Released under the GNU GPL, see LICENSE
-- We'd like to parse openscad code, with some improvements, for backwards compatability.
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TypeSynonymInstances, UndecidableInstances, ScopedTypeVariables, IncoherentInstances, ViewPatterns #-}
module Graphics.Implicit.ExtOpenScad.Definitions where
import Graphics.Implicit.Definitions
import Data.Typeable (TypeRep)
import Data.Map (Map)
import Data.Maybe (isJust)
import Control.Monad as Monad
-- Lets make it easy to change the object types we're using :)
-- | The 2D object type to be used in ExtOpenScad
type Obj2Type = SymbolicObj2
-- | The 3D object type to be used in ExtOpenScad
type Obj3Type = SymbolicObj3
-- | To look up OpenscadObj variables with a string name
type VariableLookup = Map String OpenscadObj
-- | Objects for our OpenSCAD-like language
data OpenscadObj = OUndefined
| OBool Bool
| ONum ℝ
| OList [OpenscadObj]
| OString String
| OFunc ( OpenscadObj -> OpenscadObj )
| OModule ([ComputationStateModifier] -> ArgParser ComputationStateModifier)
| OError [String]
instance Eq OpenscadObj where
(ONum a) == (ONum b) = a == b
(OBool a) == (OBool b) = a == b
(OList a) == (OList b) = a == b
(OString a) == (OString b) = a == b
_ == _ = False
-- | We'd like to be able to turn OpenscadObjs into a given Haskell type
class OTypeMirror a where
fromOObj :: OpenscadObj -> Maybe a
toOObj :: a -> OpenscadObj
instance OTypeMirror OpenscadObj where
fromOObj a = Just a
toOObj a = a
instance OTypeMirror ℝ where
fromOObj (ONum n) = Just n
fromOObj _ = Nothing
toOObj n = ONum n
instance OTypeMirror ℕ where
fromOObj (ONum n) = if n == fromIntegral (floor n) then Just (floor n) else Nothing
fromOObj _ = Nothing
toOObj n = ONum $ fromIntegral n
instance OTypeMirror Bool where
fromOObj (OBool b) = Just b
fromOObj _ = Nothing
toOObj b = OBool b
instance OTypeMirror String where
fromOObj (OString str) = Just str
fromOObj _ = Nothing
toOObj str = OString str
instance forall a. (OTypeMirror a) => OTypeMirror (Maybe a) where
fromOObj a = Just $ fromOObj a
toOObj (Just a) = toOObj a
toOObj Nothing = OUndefined
instance forall a. (OTypeMirror a) => OTypeMirror [a] where
fromOObj (OList list) = Monad.sequence . map fromOObj $ list
fromOObj _ = Nothing
toOObj list = OList $ map toOObj list
instance forall a b. (OTypeMirror a, OTypeMirror b) => OTypeMirror (a,b) where
fromOObj (OList ((fromOObj -> Just a):(fromOObj -> Just b):[])) = Just (a,b)
fromOObj _ = Nothing
toOObj (a,b) = OList [toOObj a, toOObj b]
instance forall a b c. (OTypeMirror a, OTypeMirror b, OTypeMirror c) => OTypeMirror (a,b,c) where
fromOObj (OList ((fromOObj -> Just a):(fromOObj -> Just b):(fromOObj -> Just c):[])) =
Just (a,b,c)
fromOObj _ = Nothing
toOObj (a,b,c) = OList [toOObj a, toOObj b, toOObj c]
instance forall a b. (OTypeMirror a, OTypeMirror b) => OTypeMirror (a -> b) where
fromOObj (OFunc f) = Just $ \input ->
let
oInput = toOObj input
oOutput = f oInput
output = fromOObj oOutput :: Maybe b
in case output of
Just out -> out
Nothing -> error $ "coercing OpenscadObj to a -> b isn't always safe; use a -> Maybe b"
++ " (trace: " ++ show oInput ++ " -> " ++ show oOutput ++ " )"
fromOObj _ = Nothing
toOObj f = OFunc $ \oObj ->
case fromOObj oObj :: Maybe a of
Nothing -> OError ["bad input type"]
Just obj -> toOObj $ f obj
instance forall a b. (OTypeMirror a, OTypeMirror b) => OTypeMirror (Either a b) where
fromOObj (fromOObj -> Just (x :: a)) = Just $ Left x
fromOObj (fromOObj -> Just (x :: b)) = Just $ Right x
fromOObj _ = Nothing
toOObj (Right x) = toOObj x
toOObj (Left x) = toOObj x
objTypeStr (OUndefined) = "Undefined"
objTypeStr (OBool _ ) = "Bool"
objTypeStr (ONum _ ) = "Number"
objTypeStr (OList _ ) = "List"
objTypeStr (OString _ ) = "String"
objTypeStr (OFunc _ ) = "Function"
objTypeStr (OModule _ ) = "Module"
objTypeStr (OError _ ) = "Error"
instance Show OpenscadObj where
show OUndefined = "Undefined"
show (OBool b) = show b
show (ONum n) = show n
show (OList l) = show l
show (OString s) = show s
show (OFunc f) = "<function>"
show (OModule _) = "module"
show (OError msgs) = "Execution Error:\n" ++ foldl1 (\a b -> a ++ "\n" ++ b) msgs
-- | Handles parsing arguments to modules
data ArgParser a
-- | For actual argument entries:
-- ArgParser (argument name) (default) (doc) (next Argparser...)
= ArgParser String (Maybe OpenscadObj) String (OpenscadObj -> ArgParser a)
-- | For returns:
-- ArgParserTerminator (return value)
| ArgParserTerminator a
-- | For failure:
-- ArgParserFailIf (test) (error message) (child for if true)
| ArgParserFailIf Bool String (ArgParser a)
-- An example, then next
| ArgParserExample String (ArgParser a)
-- A string to run as a test, then invariants for the results, then next
| ArgParserTest String [TestInvariant] (ArgParser a)
deriving (Show)
data TestInvariant = EulerCharacteristic Int
deriving (Show)
type ComputationState = IO (VariableLookup, [Obj2Type], [Obj3Type])
type ComputationStateModifier = ComputationState -> ComputationState
coerceNum (ONum n) = n
coerceNum _ = sqrt (-1)
coerceBool (OBool b) = b
coerceBool _ = False