-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca) -- Copyright 2016, Julia Longtin (julial@turinglace.com) -- Released under the GNU AGPLV3+, see LICENSE module Graphics.Implicit.ExtOpenScad.Definitions (ArgParser(AP, APTest, APBranch, APTerminator, APFailIf, APExample), Symbol, Pattern(Wild, Name, ListP), Expr(LitE, Var, ListE, LamE, (:$)), StatementI(StatementI), Statement(DoNothing, NewModule, Include, Echo, If, For, ModuleCall, (:=)), OVal(ONum, OBool, OString, OList, OFunc, OUndefined, OModule,OError, OObj2, OObj3), VarLookup, FStack, TestInvariant(EulerCharacteristic), collector) where import Prelude(Eq, Show, String, Maybe, Bool(True, False), Int, IO, (==), show, map, ($), (++), undefined, all, id, zipWith, foldl1) import Graphics.Implicit.Definitions (ℝ, SymbolicObj2, SymbolicObj3) import Control.Applicative (Applicative, Alternative((<|>), empty), pure, (<*>)) import Control.Monad (Functor, Monad, fmap, (>>=), mzero, mplus, MonadPlus, liftM, ap) import Data.Map (Map) ----------------------------------------------------------------- -- | Handles parsing arguments to modules data ArgParser a -- | For actual argument entries: -- ArgParser (argument name) (default) (doc) (next Argparser...) = AP String (Maybe OVal) String (OVal -> ArgParser a) -- | For returns: -- ArgParserTerminator (return value) | APTerminator a -- | For failure: -- ArgParserFailIf (test) (error message) (child for if true) | APFailIf Bool String (ArgParser a) -- An example, then next | APExample String (ArgParser a) -- A string to run as a test, then invariants for the results, then next | APTest String [TestInvariant] (ArgParser a) -- A branch where there are a number of possibilities for the parser underneath | APBranch [ArgParser a] instance Functor ArgParser where fmap = liftM instance Applicative ArgParser where pure a = APTerminator a (<*>) = ap instance Monad ArgParser where -- We need to describe how (>>=) works. -- Let's get the hard ones out of the way first. -- ArgParser actually (AP str fallback d f) >>= g = AP str fallback d (\a -> (f a) >>= g) (APFailIf b errmsg child) >>= g = APFailIf b errmsg (child >>= g) -- These next to is easy, they just pass the work along to their child (APExample str child) >>= g = APExample str (child >>= g) (APTest str tests child) >>= g = APTest str tests (child >>= g) -- And an ArgParserTerminator happily gives away the value it contains (APTerminator a) >>= g = g a (APBranch bs) >>= g = APBranch $ map (>>= g) bs instance MonadPlus ArgParser where mzero = APFailIf True "" undefined mplus (APBranch as) (APBranch bs) = APBranch ( as ++ bs ) mplus (APBranch as) b = APBranch ( as ++ [b] ) mplus a (APBranch bs) = APBranch ( [a] ++ bs ) mplus a b = APBranch [ a , b ] instance Alternative ArgParser where (<|>) = mplus empty = mzero type Symbol = String data Pattern = Name Symbol | ListP [Pattern] | Wild | Symbol :@ Pattern deriving (Show, Eq) data Expr = Var Symbol | LitE OVal | ListE [Expr] | LamE [Pattern] Expr | Expr :$ [Expr] deriving (Show, Eq) data StatementI = StatementI Int (Statement StatementI) deriving (Show, Eq) data Statement st = Include String Bool | Pattern := Expr | Echo [Expr] | For Pattern Expr [st] | If Expr [st] [st] | NewModule Symbol [(Symbol, Maybe Expr)] [st] | ModuleCall Symbol [(Maybe Symbol, Expr)] [st] | DoNothing deriving (Show, Eq) -- | Objects for our OpenSCAD-like language data OVal = OUndefined | OError [String] | OBool Bool | ONum ℝ | OList [OVal] | OString String | OFunc (OVal -> OVal) | OModule ([OVal] -> ArgParser (IO [OVal])) | OObj3 SymbolicObj3 | OObj2 SymbolicObj2 instance Eq OVal where (OBool a) == (OBool b) = a == b (ONum a) == (ONum b) = a == b (OList a) == (OList b) = all id $ zipWith (==) a b (OString a) == (OString b) = a == b _ == _ = False instance Show OVal 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 _) = "" show (OModule _) = "module" show (OError msgs) = "Execution Error:\n" ++ foldl1 (\a b -> a ++ "\n" ++ b) msgs show (OObj2 obj) = "" show (OObj3 obj) = "" type VarLookup = Map String OVal type FStack = [OVal] collector :: Symbol -> [Expr] -> Expr collector _ [x] = x collector s l = Var s :$ [ListE l] data TestInvariant = EulerCharacteristic Int deriving (Show)